1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <stdlib.h> 6 #include <stdint.h> 7 #include <inttypes.h> 8 #include <stdio.h> 9 #include <string.h> 10 #include <termios.h> 11 #include <errno.h> 12 13 #include <cmdline_rdline.h> 14 #include <cmdline_parse.h> 15 #include <cmdline_parse_string.h> 16 #include <cmdline_parse_num.h> 17 #include <cmdline_socket.h> 18 #include <cmdline.h> 19 20 #include "vm_power_cli.h" 21 #include "channel_manager.h" 22 #include "channel_monitor.h" 23 #include "power_manager.h" 24 #include "channel_commands.h" 25 26 struct cmd_quit_result { 27 cmdline_fixed_string_t quit; 28 }; 29 30 static void cmd_quit_parsed(__rte_unused void *parsed_result, 31 struct cmdline *cl, 32 __rte_unused void *data) 33 { 34 channel_monitor_exit(); 35 channel_manager_exit(); 36 power_manager_exit(); 37 cmdline_quit(cl); 38 } 39 40 cmdline_parse_token_string_t cmd_quit_quit = 41 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 42 43 cmdline_parse_inst_t cmd_quit = { 44 .f = cmd_quit_parsed, /* function to call */ 45 .data = NULL, /* 2nd arg of func */ 46 .help_str = "close the application", 47 .tokens = { /* token list, NULL terminated */ 48 (void *)&cmd_quit_quit, 49 NULL, 50 }, 51 }; 52 53 /* *** VM operations *** */ 54 struct cmd_show_vm_result { 55 cmdline_fixed_string_t show_vm; 56 cmdline_fixed_string_t vm_name; 57 }; 58 59 static void 60 cmd_show_vm_parsed(void *parsed_result, struct cmdline *cl, 61 __rte_unused void *data) 62 { 63 struct cmd_show_vm_result *res = parsed_result; 64 struct vm_info info; 65 unsigned i; 66 67 if (get_info_vm(res->vm_name, &info) != 0) 68 return; 69 cmdline_printf(cl, "VM: '%s', status = ", info.name); 70 if (info.status == CHANNEL_MGR_VM_ACTIVE) 71 cmdline_printf(cl, "ACTIVE\n"); 72 else 73 cmdline_printf(cl, "INACTIVE\n"); 74 cmdline_printf(cl, "Channels %u\n", info.num_channels); 75 for (i = 0; i < info.num_channels; i++) { 76 cmdline_printf(cl, " [%u]: %s, status = ", i, 77 info.channels[i].channel_path); 78 switch (info.channels[i].status) { 79 case CHANNEL_MGR_CHANNEL_CONNECTED: 80 cmdline_printf(cl, "CONNECTED\n"); 81 break; 82 case CHANNEL_MGR_CHANNEL_DISCONNECTED: 83 cmdline_printf(cl, "DISCONNECTED\n"); 84 break; 85 case CHANNEL_MGR_CHANNEL_DISABLED: 86 cmdline_printf(cl, "DISABLED\n"); 87 break; 88 case CHANNEL_MGR_CHANNEL_PROCESSING: 89 cmdline_printf(cl, "PROCESSING\n"); 90 break; 91 default: 92 cmdline_printf(cl, "UNKNOWN\n"); 93 break; 94 } 95 } 96 cmdline_printf(cl, "Virtual CPU(s): %u\n", info.num_vcpus); 97 for (i = 0; i < info.num_vcpus; i++) { 98 cmdline_printf(cl, " [%u]: Physical CPU %d\n", i, 99 info.pcpu_map[i]); 100 } 101 } 102 103 104 105 cmdline_parse_token_string_t cmd_vm_show = 106 TOKEN_STRING_INITIALIZER(struct cmd_show_vm_result, 107 show_vm, "show_vm"); 108 cmdline_parse_token_string_t cmd_show_vm_name = 109 TOKEN_STRING_INITIALIZER(struct cmd_show_vm_result, 110 vm_name, NULL); 111 112 cmdline_parse_inst_t cmd_show_vm_set = { 113 .f = cmd_show_vm_parsed, 114 .data = NULL, 115 .help_str = "show_vm <vm_name>, prints the information on the " 116 "specified VM(s), the information lists the number of vCPUS, the " 117 "pinning to pCPU(s) as a bit mask, along with any communication " 118 "channels associated with each VM", 119 .tokens = { 120 (void *)&cmd_vm_show, 121 (void *)&cmd_show_vm_name, 122 NULL, 123 }, 124 }; 125 126 /* *** vCPU to pCPU mapping operations *** */ 127 128 129 struct cmd_set_pcpu_result { 130 cmdline_fixed_string_t set_pcpu; 131 cmdline_fixed_string_t vm_name; 132 uint8_t vcpu; 133 uint8_t core; 134 }; 135 136 static void 137 cmd_set_pcpu_parsed(void *parsed_result, struct cmdline *cl, 138 __rte_unused void *data) 139 { 140 struct cmd_set_pcpu_result *res = parsed_result; 141 142 if (set_pcpu(res->vm_name, res->vcpu, res->core) == 0) 143 cmdline_printf(cl, "Pinned vCPU(%"PRId8") to pCPU core " 144 "%"PRId8")\n", res->vcpu, res->core); 145 else 146 cmdline_printf(cl, "Unable to pin vCPU(%"PRId8") to pCPU core " 147 "%"PRId8")\n", res->vcpu, res->core); 148 } 149 150 cmdline_parse_token_string_t cmd_set_pcpu = 151 TOKEN_STRING_INITIALIZER(struct cmd_set_pcpu_result, 152 set_pcpu, "set_pcpu"); 153 cmdline_parse_token_string_t cmd_set_pcpu_vm_name = 154 TOKEN_STRING_INITIALIZER(struct cmd_set_pcpu_result, 155 vm_name, NULL); 156 cmdline_parse_token_num_t set_pcpu_vcpu = 157 TOKEN_NUM_INITIALIZER(struct cmd_set_pcpu_result, 158 vcpu, RTE_UINT8); 159 cmdline_parse_token_num_t set_pcpu_core = 160 TOKEN_NUM_INITIALIZER(struct cmd_set_pcpu_result, 161 core, RTE_UINT64); 162 163 164 cmdline_parse_inst_t cmd_set_pcpu_set = { 165 .f = cmd_set_pcpu_parsed, 166 .data = NULL, 167 .help_str = "set_pcpu <vm_name> <vcpu> <pcpu>, Set the binding " 168 "of Virtual CPU on VM to the Physical CPU.", 169 .tokens = { 170 (void *)&cmd_set_pcpu, 171 (void *)&cmd_set_pcpu_vm_name, 172 (void *)&set_pcpu_vcpu, 173 (void *)&set_pcpu_core, 174 NULL, 175 }, 176 }; 177 178 struct cmd_vm_op_result { 179 cmdline_fixed_string_t op_vm; 180 cmdline_fixed_string_t vm_name; 181 }; 182 183 static void 184 cmd_vm_op_parsed(void *parsed_result, struct cmdline *cl, 185 __rte_unused void *data) 186 { 187 struct cmd_vm_op_result *res = parsed_result; 188 189 if (!strcmp(res->op_vm, "add_vm")) { 190 if (add_vm(res->vm_name) < 0) 191 cmdline_printf(cl, "Unable to add VM '%s'\n", res->vm_name); 192 } else if (remove_vm(res->vm_name) < 0) 193 cmdline_printf(cl, "Unable to remove VM '%s'\n", res->vm_name); 194 } 195 196 cmdline_parse_token_string_t cmd_vm_op = 197 TOKEN_STRING_INITIALIZER(struct cmd_vm_op_result, 198 op_vm, "add_vm#rm_vm"); 199 cmdline_parse_token_string_t cmd_vm_name = 200 TOKEN_STRING_INITIALIZER(struct cmd_vm_op_result, 201 vm_name, NULL); 202 203 cmdline_parse_inst_t cmd_vm_op_set = { 204 .f = cmd_vm_op_parsed, 205 .data = NULL, 206 .help_str = "add_vm|rm_vm <name>, add a VM for " 207 "subsequent operations with the CLI or remove a previously added " 208 "VM from the VM Power Manager", 209 .tokens = { 210 (void *)&cmd_vm_op, 211 (void *)&cmd_vm_name, 212 NULL, 213 }, 214 }; 215 216 /* *** VM channel operations *** */ 217 struct cmd_channels_op_result { 218 cmdline_fixed_string_t op; 219 cmdline_fixed_string_t vm_name; 220 cmdline_fixed_string_t channel_list; 221 }; 222 static void 223 cmd_channels_op_parsed(void *parsed_result, struct cmdline *cl, 224 __rte_unused void *data) 225 { 226 unsigned num_channels = 0, channel_num, i; 227 int channels_added; 228 unsigned int channel_list[RTE_MAX_LCORE]; 229 char *token, *remaining, *tail_ptr; 230 struct cmd_channels_op_result *res = parsed_result; 231 232 if (!strcmp(res->channel_list, "all")) { 233 channels_added = add_all_channels(res->vm_name); 234 cmdline_printf(cl, "Added %d channels for VM '%s'\n", 235 channels_added, res->vm_name); 236 return; 237 } 238 239 remaining = res->channel_list; 240 while (1) { 241 if (remaining == NULL || remaining[0] == '\0') 242 break; 243 244 token = strsep(&remaining, ","); 245 if (token == NULL) 246 break; 247 errno = 0; 248 channel_num = (unsigned)strtol(token, &tail_ptr, 10); 249 if ((errno != 0) || tail_ptr == NULL || (*tail_ptr != '\0')) 250 break; 251 252 if (channel_num == RTE_MAX_LCORE) { 253 cmdline_printf(cl, "Channel number '%u' exceeds the maximum number " 254 "of allowable channels(%u) for VM '%s'\n", channel_num, 255 RTE_MAX_LCORE, res->vm_name); 256 return; 257 } 258 channel_list[num_channels++] = channel_num; 259 } 260 for (i = 0; i < num_channels; i++) 261 cmdline_printf(cl, "[%u]: Adding channel %u\n", i, channel_list[i]); 262 263 channels_added = add_channels(res->vm_name, channel_list, 264 num_channels); 265 cmdline_printf(cl, "Enabled %d channels for '%s'\n", channels_added, 266 res->vm_name); 267 } 268 269 cmdline_parse_token_string_t cmd_channels_op = 270 TOKEN_STRING_INITIALIZER(struct cmd_channels_op_result, 271 op, "add_channels"); 272 cmdline_parse_token_string_t cmd_channels_vm_name = 273 TOKEN_STRING_INITIALIZER(struct cmd_channels_op_result, 274 vm_name, NULL); 275 cmdline_parse_token_string_t cmd_channels_list = 276 TOKEN_STRING_INITIALIZER(struct cmd_channels_op_result, 277 channel_list, NULL); 278 279 cmdline_parse_inst_t cmd_channels_op_set = { 280 .f = cmd_channels_op_parsed, 281 .data = NULL, 282 .help_str = "add_channels <vm_name> <list>|all, add " 283 "communication channels for the specified VM, the " 284 "virtio channels must be enabled in the VM " 285 "configuration(qemu/libvirt) and the associated VM must be active. " 286 "<list> is a comma-separated list of channel numbers to add, using " 287 "the keyword 'all' will attempt to add all channels for the VM", 288 .tokens = { 289 (void *)&cmd_channels_op, 290 (void *)&cmd_channels_vm_name, 291 (void *)&cmd_channels_list, 292 NULL, 293 }, 294 }; 295 296 struct cmd_set_query_result { 297 cmdline_fixed_string_t set_query; 298 cmdline_fixed_string_t vm_name; 299 cmdline_fixed_string_t query_status; 300 }; 301 302 static void 303 cmd_set_query_parsed(void *parsed_result, 304 __rte_unused struct cmdline *cl, 305 __rte_unused void *data) 306 { 307 struct cmd_set_query_result *res = parsed_result; 308 309 if (!strcmp(res->query_status, "enable")) { 310 if (set_query_status(res->vm_name, true) < 0) 311 cmdline_printf(cl, "Unable to allow query for VM '%s'\n", 312 res->vm_name); 313 } else if (!strcmp(res->query_status, "disable")) { 314 if (set_query_status(res->vm_name, false) < 0) 315 cmdline_printf(cl, "Unable to disallow query for VM '%s'\n", 316 res->vm_name); 317 } 318 } 319 320 cmdline_parse_token_string_t cmd_set_query = 321 TOKEN_STRING_INITIALIZER(struct cmd_set_query_result, 322 set_query, "set_query"); 323 cmdline_parse_token_string_t cmd_set_query_vm_name = 324 TOKEN_STRING_INITIALIZER(struct cmd_set_query_result, 325 vm_name, NULL); 326 cmdline_parse_token_string_t cmd_set_query_status = 327 TOKEN_STRING_INITIALIZER(struct cmd_set_query_result, 328 query_status, "enable#disable"); 329 330 cmdline_parse_inst_t cmd_set_query_set = { 331 .f = cmd_set_query_parsed, 332 .data = NULL, 333 .help_str = "set_query <vm_name> <enable|disable>, allow or disallow queries" 334 " for the specified VM", 335 .tokens = { 336 (void *)&cmd_set_query, 337 (void *)&cmd_set_query_vm_name, 338 (void *)&cmd_set_query_status, 339 NULL, 340 }, 341 }; 342 343 struct cmd_channels_status_op_result { 344 cmdline_fixed_string_t op; 345 cmdline_fixed_string_t vm_name; 346 cmdline_fixed_string_t channel_list; 347 cmdline_fixed_string_t status; 348 }; 349 350 static void 351 cmd_channels_status_op_parsed(void *parsed_result, struct cmdline *cl, 352 __rte_unused void *data) 353 { 354 unsigned num_channels = 0, channel_num; 355 int changed; 356 unsigned int channel_list[RTE_MAX_LCORE]; 357 char *token, *remaining, *tail_ptr; 358 struct cmd_channels_status_op_result *res = parsed_result; 359 enum channel_status status; 360 361 if (!strcmp(res->status, "enabled")) 362 status = CHANNEL_MGR_CHANNEL_CONNECTED; 363 else 364 status = CHANNEL_MGR_CHANNEL_DISABLED; 365 366 if (!strcmp(res->channel_list, "all")) { 367 changed = set_channel_status_all(res->vm_name, status); 368 cmdline_printf(cl, "Updated status of %d channels " 369 "for VM '%s'\n", changed, res->vm_name); 370 return; 371 } 372 remaining = res->channel_list; 373 while (1) { 374 if (remaining == NULL || remaining[0] == '\0') 375 break; 376 token = strsep(&remaining, ","); 377 if (token == NULL) 378 break; 379 errno = 0; 380 channel_num = (unsigned)strtol(token, &tail_ptr, 10); 381 if ((errno != 0) || tail_ptr == NULL || (*tail_ptr != '\0')) 382 break; 383 384 if (channel_num == RTE_MAX_LCORE) { 385 cmdline_printf(cl, "%u exceeds the maximum number of allowable " 386 "channels(%u) for VM '%s'\n", channel_num, 387 RTE_MAX_LCORE, res->vm_name); 388 return; 389 } 390 channel_list[num_channels++] = channel_num; 391 } 392 changed = set_channel_status(res->vm_name, channel_list, num_channels, 393 status); 394 cmdline_printf(cl, "Updated status of %d channels " 395 "for VM '%s'\n", changed, res->vm_name); 396 } 397 398 cmdline_parse_token_string_t cmd_channels_status_op = 399 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 400 op, "set_channel_status"); 401 cmdline_parse_token_string_t cmd_channels_status_vm_name = 402 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 403 vm_name, NULL); 404 cmdline_parse_token_string_t cmd_channels_status_list = 405 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 406 channel_list, NULL); 407 cmdline_parse_token_string_t cmd_channels_status = 408 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 409 status, "enabled#disabled"); 410 411 cmdline_parse_inst_t cmd_channels_status_op_set = { 412 .f = cmd_channels_status_op_parsed, 413 .data = NULL, 414 .help_str = "set_channel_status <vm_name> <list>|all enabled|disabled, " 415 " enable or disable the communication channels in " 416 "list(comma-separated) for the specified VM, alternatively " 417 "list can be replaced with keyword 'all'. " 418 "Disabled channels will still receive packets on the host, " 419 "however the commands they specify will be ignored. " 420 "Set status to 'enabled' to begin processing requests again.", 421 .tokens = { 422 (void *)&cmd_channels_status_op, 423 (void *)&cmd_channels_status_vm_name, 424 (void *)&cmd_channels_status_list, 425 (void *)&cmd_channels_status, 426 NULL, 427 }, 428 }; 429 430 /* *** CPU Frequency operations *** */ 431 struct cmd_show_cpu_freq_result { 432 cmdline_fixed_string_t show_cpu_freq; 433 uint8_t core_num; 434 }; 435 436 static void 437 cmd_show_cpu_freq_parsed(void *parsed_result, struct cmdline *cl, 438 __rte_unused void *data) 439 { 440 struct cmd_show_cpu_freq_result *res = parsed_result; 441 uint32_t curr_freq = power_manager_get_current_frequency(res->core_num); 442 443 if (curr_freq == 0) { 444 cmdline_printf(cl, "Unable to get frequency for core %u\n", 445 res->core_num); 446 return; 447 } 448 cmdline_printf(cl, "Core %u frequency: %"PRId32"\n", res->core_num, 449 curr_freq); 450 } 451 452 cmdline_parse_token_string_t cmd_show_cpu_freq = 453 TOKEN_STRING_INITIALIZER(struct cmd_show_cpu_freq_result, 454 show_cpu_freq, "show_cpu_freq"); 455 456 cmdline_parse_token_num_t cmd_show_cpu_freq_core_num = 457 TOKEN_NUM_INITIALIZER(struct cmd_show_cpu_freq_result, 458 core_num, RTE_UINT8); 459 460 cmdline_parse_inst_t cmd_show_cpu_freq_set = { 461 .f = cmd_show_cpu_freq_parsed, 462 .data = NULL, 463 .help_str = "Get the current frequency for the specified core", 464 .tokens = { 465 (void *)&cmd_show_cpu_freq, 466 (void *)&cmd_show_cpu_freq_core_num, 467 NULL, 468 }, 469 }; 470 471 struct cmd_set_cpu_freq_result { 472 cmdline_fixed_string_t set_cpu_freq; 473 uint8_t core_num; 474 cmdline_fixed_string_t cmd; 475 }; 476 477 static void 478 cmd_set_cpu_freq_parsed(void *parsed_result, struct cmdline *cl, 479 __rte_unused void *data) 480 { 481 int ret = -1; 482 struct cmd_set_cpu_freq_result *res = parsed_result; 483 484 if (!strcmp(res->cmd , "up")) 485 ret = power_manager_scale_core_up(res->core_num); 486 else if (!strcmp(res->cmd , "down")) 487 ret = power_manager_scale_core_down(res->core_num); 488 else if (!strcmp(res->cmd , "min")) 489 ret = power_manager_scale_core_min(res->core_num); 490 else if (!strcmp(res->cmd , "max")) 491 ret = power_manager_scale_core_max(res->core_num); 492 else if (!strcmp(res->cmd, "enable_turbo")) 493 ret = power_manager_enable_turbo_core(res->core_num); 494 else if (!strcmp(res->cmd, "disable_turbo")) 495 ret = power_manager_disable_turbo_core(res->core_num); 496 if (ret < 0) { 497 cmdline_printf(cl, "Error scaling core(%u) '%s'\n", res->core_num, 498 res->cmd); 499 } 500 } 501 502 cmdline_parse_token_string_t cmd_set_cpu_freq = 503 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 504 set_cpu_freq, "set_cpu_freq"); 505 cmdline_parse_token_num_t cmd_set_cpu_freq_core_num = 506 TOKEN_NUM_INITIALIZER(struct cmd_set_cpu_freq_result, 507 core_num, RTE_UINT8); 508 cmdline_parse_token_string_t cmd_set_cpu_freq_cmd_cmd = 509 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 510 cmd, "up#down#min#max#enable_turbo#disable_turbo"); 511 512 cmdline_parse_inst_t cmd_set_cpu_freq_set = { 513 .f = cmd_set_cpu_freq_parsed, 514 .data = NULL, 515 .help_str = "set_cpu_freq <core_num> <up|down|min|max|enable_turbo|disable_turbo>, adjust the current " 516 "frequency for the specified core", 517 .tokens = { 518 (void *)&cmd_set_cpu_freq, 519 (void *)&cmd_set_cpu_freq_core_num, 520 (void *)&cmd_set_cpu_freq_cmd_cmd, 521 NULL, 522 }, 523 }; 524 525 cmdline_parse_ctx_t main_ctx[] = { 526 (cmdline_parse_inst_t *)&cmd_quit, 527 (cmdline_parse_inst_t *)&cmd_vm_op_set, 528 (cmdline_parse_inst_t *)&cmd_channels_op_set, 529 (cmdline_parse_inst_t *)&cmd_channels_status_op_set, 530 (cmdline_parse_inst_t *)&cmd_show_vm_set, 531 (cmdline_parse_inst_t *)&cmd_show_cpu_freq_set, 532 (cmdline_parse_inst_t *)&cmd_set_cpu_freq_set, 533 (cmdline_parse_inst_t *)&cmd_set_pcpu_set, 534 (cmdline_parse_inst_t *)&cmd_set_query_set, 535 NULL, 536 }; 537 538 void 539 run_cli(__rte_unused void *arg) 540 { 541 struct cmdline *cl; 542 543 cl = cmdline_stdin_new(main_ctx, "vmpower> "); 544 if (cl == NULL) 545 return; 546 547 cmdline_interact(cl); 548 cmdline_stdin_exit(cl); 549 } 550