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(__attribute__((unused)) void *parsed_result, 31 struct cmdline *cl, 32 __attribute__((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 __attribute__((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 __attribute__((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, UINT8); 159 cmdline_parse_token_num_t set_pcpu_core = 160 TOKEN_NUM_INITIALIZER(struct cmd_set_pcpu_result, 161 core, 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 __attribute__((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 __attribute__((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_channels_status_op_result { 297 cmdline_fixed_string_t op; 298 cmdline_fixed_string_t vm_name; 299 cmdline_fixed_string_t channel_list; 300 cmdline_fixed_string_t status; 301 }; 302 303 static void 304 cmd_channels_status_op_parsed(void *parsed_result, struct cmdline *cl, 305 __attribute__((unused)) void *data) 306 { 307 unsigned num_channels = 0, channel_num; 308 int changed; 309 unsigned int channel_list[RTE_MAX_LCORE]; 310 char *token, *remaining, *tail_ptr; 311 struct cmd_channels_status_op_result *res = parsed_result; 312 enum channel_status status; 313 314 if (!strcmp(res->status, "enabled")) 315 status = CHANNEL_MGR_CHANNEL_CONNECTED; 316 else 317 status = CHANNEL_MGR_CHANNEL_DISABLED; 318 319 if (!strcmp(res->channel_list, "all")) { 320 changed = set_channel_status_all(res->vm_name, status); 321 cmdline_printf(cl, "Updated status of %d channels " 322 "for VM '%s'\n", changed, res->vm_name); 323 return; 324 } 325 remaining = res->channel_list; 326 while (1) { 327 if (remaining == NULL || remaining[0] == '\0') 328 break; 329 token = strsep(&remaining, ","); 330 if (token == NULL) 331 break; 332 errno = 0; 333 channel_num = (unsigned)strtol(token, &tail_ptr, 10); 334 if ((errno != 0) || tail_ptr == NULL || (*tail_ptr != '\0')) 335 break; 336 337 if (channel_num == RTE_MAX_LCORE) { 338 cmdline_printf(cl, "%u exceeds the maximum number of allowable " 339 "channels(%u) for VM '%s'\n", channel_num, 340 RTE_MAX_LCORE, res->vm_name); 341 return; 342 } 343 channel_list[num_channels++] = channel_num; 344 } 345 changed = set_channel_status(res->vm_name, channel_list, num_channels, 346 status); 347 cmdline_printf(cl, "Updated status of %d channels " 348 "for VM '%s'\n", changed, res->vm_name); 349 } 350 351 cmdline_parse_token_string_t cmd_channels_status_op = 352 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 353 op, "set_channel_status"); 354 cmdline_parse_token_string_t cmd_channels_status_vm_name = 355 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 356 vm_name, NULL); 357 cmdline_parse_token_string_t cmd_channels_status_list = 358 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 359 channel_list, NULL); 360 cmdline_parse_token_string_t cmd_channels_status = 361 TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result, 362 status, "enabled#disabled"); 363 364 cmdline_parse_inst_t cmd_channels_status_op_set = { 365 .f = cmd_channels_status_op_parsed, 366 .data = NULL, 367 .help_str = "set_channel_status <vm_name> <list>|all enabled|disabled, " 368 " enable or disable the communication channels in " 369 "list(comma-separated) for the specified VM, alternatively " 370 "list can be replaced with keyword 'all'. " 371 "Disabled channels will still receive packets on the host, " 372 "however the commands they specify will be ignored. " 373 "Set status to 'enabled' to begin processing requests again.", 374 .tokens = { 375 (void *)&cmd_channels_status_op, 376 (void *)&cmd_channels_status_vm_name, 377 (void *)&cmd_channels_status_list, 378 (void *)&cmd_channels_status, 379 NULL, 380 }, 381 }; 382 383 /* *** CPU Frequency operations *** */ 384 struct cmd_show_cpu_freq_result { 385 cmdline_fixed_string_t show_cpu_freq; 386 uint8_t core_num; 387 }; 388 389 static void 390 cmd_show_cpu_freq_parsed(void *parsed_result, struct cmdline *cl, 391 __attribute__((unused)) void *data) 392 { 393 struct cmd_show_cpu_freq_result *res = parsed_result; 394 uint32_t curr_freq = power_manager_get_current_frequency(res->core_num); 395 396 if (curr_freq == 0) { 397 cmdline_printf(cl, "Unable to get frequency for core %u\n", 398 res->core_num); 399 return; 400 } 401 cmdline_printf(cl, "Core %u frequency: %"PRId32"\n", res->core_num, 402 curr_freq); 403 } 404 405 cmdline_parse_token_string_t cmd_show_cpu_freq = 406 TOKEN_STRING_INITIALIZER(struct cmd_show_cpu_freq_result, 407 show_cpu_freq, "show_cpu_freq"); 408 409 cmdline_parse_token_num_t cmd_show_cpu_freq_core_num = 410 TOKEN_NUM_INITIALIZER(struct cmd_show_cpu_freq_result, 411 core_num, UINT8); 412 413 cmdline_parse_inst_t cmd_show_cpu_freq_set = { 414 .f = cmd_show_cpu_freq_parsed, 415 .data = NULL, 416 .help_str = "Get the current frequency for the specified core", 417 .tokens = { 418 (void *)&cmd_show_cpu_freq, 419 (void *)&cmd_show_cpu_freq_core_num, 420 NULL, 421 }, 422 }; 423 424 struct cmd_set_cpu_freq_result { 425 cmdline_fixed_string_t set_cpu_freq; 426 uint8_t core_num; 427 cmdline_fixed_string_t cmd; 428 }; 429 430 static void 431 cmd_set_cpu_freq_parsed(void *parsed_result, struct cmdline *cl, 432 __attribute__((unused)) void *data) 433 { 434 int ret = -1; 435 struct cmd_set_cpu_freq_result *res = parsed_result; 436 437 if (!strcmp(res->cmd , "up")) 438 ret = power_manager_scale_core_up(res->core_num); 439 else if (!strcmp(res->cmd , "down")) 440 ret = power_manager_scale_core_down(res->core_num); 441 else if (!strcmp(res->cmd , "min")) 442 ret = power_manager_scale_core_min(res->core_num); 443 else if (!strcmp(res->cmd , "max")) 444 ret = power_manager_scale_core_max(res->core_num); 445 else if (!strcmp(res->cmd, "enable_turbo")) 446 ret = power_manager_enable_turbo_core(res->core_num); 447 else if (!strcmp(res->cmd, "disable_turbo")) 448 ret = power_manager_disable_turbo_core(res->core_num); 449 if (ret < 0) { 450 cmdline_printf(cl, "Error scaling core(%u) '%s'\n", res->core_num, 451 res->cmd); 452 } 453 } 454 455 cmdline_parse_token_string_t cmd_set_cpu_freq = 456 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 457 set_cpu_freq, "set_cpu_freq"); 458 cmdline_parse_token_num_t cmd_set_cpu_freq_core_num = 459 TOKEN_NUM_INITIALIZER(struct cmd_set_cpu_freq_result, 460 core_num, UINT8); 461 cmdline_parse_token_string_t cmd_set_cpu_freq_cmd_cmd = 462 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 463 cmd, "up#down#min#max#enable_turbo#disable_turbo"); 464 465 cmdline_parse_inst_t cmd_set_cpu_freq_set = { 466 .f = cmd_set_cpu_freq_parsed, 467 .data = NULL, 468 .help_str = "set_cpu_freq <core_num> <up|down|min|max|enable_turbo|disable_turbo>, adjust the current " 469 "frequency for the specified core", 470 .tokens = { 471 (void *)&cmd_set_cpu_freq, 472 (void *)&cmd_set_cpu_freq_core_num, 473 (void *)&cmd_set_cpu_freq_cmd_cmd, 474 NULL, 475 }, 476 }; 477 478 cmdline_parse_ctx_t main_ctx[] = { 479 (cmdline_parse_inst_t *)&cmd_quit, 480 (cmdline_parse_inst_t *)&cmd_vm_op_set, 481 (cmdline_parse_inst_t *)&cmd_channels_op_set, 482 (cmdline_parse_inst_t *)&cmd_channels_status_op_set, 483 (cmdline_parse_inst_t *)&cmd_show_vm_set, 484 (cmdline_parse_inst_t *)&cmd_show_cpu_freq_set, 485 (cmdline_parse_inst_t *)&cmd_set_cpu_freq_set, 486 (cmdline_parse_inst_t *)&cmd_set_pcpu_set, 487 NULL, 488 }; 489 490 void 491 run_cli(__attribute__((unused)) void *arg) 492 { 493 struct cmdline *cl; 494 495 cl = cmdline_stdin_new(main_ctx, "vmpower> "); 496 if (cl == NULL) 497 return; 498 499 cmdline_interact(cl); 500 cmdline_stdin_exit(cl); 501 } 502