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