1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 6 #include <stdint.h> 7 #include <string.h> 8 #include <stdio.h> 9 #include <termios.h> 10 11 #include <cmdline_rdline.h> 12 #include <cmdline_parse.h> 13 #include <cmdline_parse_string.h> 14 #include <cmdline_parse_num.h> 15 #include <cmdline_socket.h> 16 #include <cmdline.h> 17 #include <rte_log.h> 18 #include <rte_lcore.h> 19 #include <rte_ethdev.h> 20 21 #include <rte_power.h> 22 #include <guest_channel.h> 23 24 #include "vm_power_cli_guest.h" 25 26 27 #define CHANNEL_PATH "/dev/virtio-ports/virtio.serial.port.poweragent" 28 29 30 #define RTE_LOGTYPE_GUEST_CLI RTE_LOGTYPE_USER1 31 32 struct cmd_quit_result { 33 cmdline_fixed_string_t quit; 34 }; 35 36 union PFID { 37 struct rte_ether_addr addr; 38 uint64_t pfid; 39 }; 40 41 static struct channel_packet policy; 42 43 struct channel_packet * 44 get_policy(void) 45 { 46 return &policy; 47 } 48 49 int 50 set_policy_mac(int port, int idx) 51 { 52 struct channel_packet *policy; 53 union PFID pfid; 54 int ret; 55 56 /* Use port MAC address as the vfid */ 57 ret = rte_eth_macaddr_get(port, &pfid.addr); 58 if (ret != 0) { 59 printf("Failed to get device (port %u) MAC address: %s\n", 60 port, rte_strerror(-ret)); 61 return ret; 62 } 63 64 printf("Port %u MAC: %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":" 65 "%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 "\n", 66 port, 67 pfid.addr.addr_bytes[0], pfid.addr.addr_bytes[1], 68 pfid.addr.addr_bytes[2], pfid.addr.addr_bytes[3], 69 pfid.addr.addr_bytes[4], pfid.addr.addr_bytes[5]); 70 policy = get_policy(); 71 policy->vfid[idx] = pfid.pfid; 72 return 0; 73 } 74 75 int 76 set_policy_defaults(struct channel_packet *pkt) 77 { 78 int ret; 79 80 ret = set_policy_mac(0, 0); 81 if (ret != 0) 82 pkt->nb_mac_to_monitor = 0; 83 else 84 pkt->nb_mac_to_monitor = 1; 85 86 pkt->t_boost_status.tbEnabled = false; 87 88 pkt->vcpu_to_control[0] = 0; 89 pkt->vcpu_to_control[1] = 1; 90 pkt->num_vcpu = 2; 91 /* Dummy Population. */ 92 pkt->traffic_policy.min_packet_thresh = 96000; 93 pkt->traffic_policy.avg_max_packet_thresh = 1800000; 94 pkt->traffic_policy.max_max_packet_thresh = 2000000; 95 96 pkt->timer_policy.busy_hours[0] = 3; 97 pkt->timer_policy.busy_hours[1] = 4; 98 pkt->timer_policy.busy_hours[2] = 5; 99 pkt->timer_policy.quiet_hours[0] = 11; 100 pkt->timer_policy.quiet_hours[1] = 12; 101 pkt->timer_policy.quiet_hours[2] = 13; 102 103 pkt->timer_policy.hours_to_use_traffic_profile[0] = 8; 104 pkt->timer_policy.hours_to_use_traffic_profile[1] = 10; 105 106 pkt->core_type = CORE_TYPE_VIRTUAL; 107 pkt->workload = LOW; 108 pkt->policy_to_use = TIME; 109 pkt->command = PKT_POLICY; 110 strlcpy(pkt->vm_name, "ubuntu2", sizeof(pkt->vm_name)); 111 112 return 0; 113 } 114 115 static void cmd_quit_parsed(__rte_unused void *parsed_result, 116 __rte_unused struct cmdline *cl, 117 __rte_unused void *data) 118 { 119 unsigned lcore_id; 120 121 RTE_LCORE_FOREACH(lcore_id) { 122 rte_power_exit(lcore_id); 123 } 124 cmdline_quit(cl); 125 } 126 127 cmdline_parse_token_string_t cmd_quit_quit = 128 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 129 130 cmdline_parse_inst_t cmd_quit = { 131 .f = cmd_quit_parsed, /* function to call */ 132 .data = NULL, /* 2nd arg of func */ 133 .help_str = "close the application", 134 .tokens = { /* token list, NULL terminated */ 135 (void *)&cmd_quit_quit, 136 NULL, 137 }, 138 }; 139 140 /* *** VM operations *** */ 141 142 struct cmd_freq_list_result { 143 cmdline_fixed_string_t query_freq; 144 cmdline_fixed_string_t cpu_num; 145 }; 146 147 static int 148 query_data(struct channel_packet *pkt, unsigned int lcore_id) 149 { 150 int ret; 151 ret = rte_power_guest_channel_send_msg(pkt, lcore_id); 152 if (ret < 0) { 153 RTE_LOG(ERR, GUEST_CLI, "Error sending message.\n"); 154 return -1; 155 } 156 return 0; 157 } 158 159 static int 160 receive_freq_list(struct channel_packet_freq_list *pkt_freq_list, 161 unsigned int lcore_id) 162 { 163 int ret; 164 165 ret = rte_power_guest_channel_receive_msg(pkt_freq_list, 166 sizeof(struct channel_packet_freq_list), 167 lcore_id); 168 if (ret < 0) { 169 RTE_LOG(ERR, GUEST_CLI, "Error receiving message.\n"); 170 return -1; 171 } 172 if (pkt_freq_list->command != CPU_POWER_FREQ_LIST) { 173 RTE_LOG(ERR, GUEST_CLI, "Unexpected message received.\n"); 174 return -1; 175 } 176 return 0; 177 } 178 179 static void 180 cmd_query_freq_list_parsed(void *parsed_result, 181 __rte_unused struct cmdline *cl, 182 __rte_unused void *data) 183 { 184 struct cmd_freq_list_result *res = parsed_result; 185 unsigned int lcore_id; 186 struct channel_packet_freq_list pkt_freq_list; 187 struct channel_packet pkt; 188 bool query_list = false; 189 int ret; 190 char *ep; 191 192 memset(&pkt, 0, sizeof(struct channel_packet)); 193 memset(&pkt_freq_list, 0, sizeof(struct channel_packet_freq_list)); 194 195 if (!strcmp(res->cpu_num, "all")) { 196 197 /* Get first enabled lcore. */ 198 lcore_id = rte_get_next_lcore(-1, 199 0, 200 0); 201 if (lcore_id == RTE_MAX_LCORE) { 202 cmdline_printf(cl, "Enabled core not found.\n"); 203 return; 204 } 205 206 pkt.command = CPU_POWER_QUERY_FREQ_LIST; 207 strlcpy(pkt.vm_name, policy.vm_name, sizeof(pkt.vm_name)); 208 query_list = true; 209 } else { 210 errno = 0; 211 lcore_id = (unsigned int)strtol(res->cpu_num, &ep, 10); 212 if (errno != 0 || lcore_id >= MAX_VCPU_PER_VM || 213 ep == res->cpu_num) { 214 cmdline_printf(cl, "Invalid parameter provided.\n"); 215 return; 216 } 217 pkt.command = CPU_POWER_QUERY_FREQ; 218 strlcpy(pkt.vm_name, policy.vm_name, sizeof(pkt.vm_name)); 219 pkt.resource_id = lcore_id; 220 } 221 222 ret = query_data(&pkt, lcore_id); 223 if (ret < 0) { 224 cmdline_printf(cl, "Error during sending frequency list query.\n"); 225 return; 226 } 227 228 ret = receive_freq_list(&pkt_freq_list, lcore_id); 229 if (ret < 0) { 230 cmdline_printf(cl, "Error during frequency list reception.\n"); 231 return; 232 } 233 if (query_list) { 234 unsigned int i; 235 for (i = 0; i < pkt_freq_list.num_vcpu; ++i) 236 cmdline_printf(cl, "Frequency of [%d] vcore is %d.\n", 237 i, 238 pkt_freq_list.freq_list[i]); 239 } else { 240 cmdline_printf(cl, "Frequency of [%d] vcore is %d.\n", 241 lcore_id, 242 pkt_freq_list.freq_list[lcore_id]); 243 } 244 } 245 246 cmdline_parse_token_string_t cmd_query_freq_token = 247 TOKEN_STRING_INITIALIZER(struct cmd_freq_list_result, query_freq, "query_cpu_freq"); 248 cmdline_parse_token_string_t cmd_query_freq_cpu_num_token = 249 TOKEN_STRING_INITIALIZER(struct cmd_freq_list_result, cpu_num, NULL); 250 251 cmdline_parse_inst_t cmd_query_freq_list = { 252 .f = cmd_query_freq_list_parsed, /* function to call */ 253 .data = NULL, /* 2nd arg of func */ 254 .help_str = "query_cpu_freq <core_num>|all, request" 255 " information regarding virtual core frequencies." 256 " The keyword 'all' will query list of all vcores for the VM", 257 .tokens = { /* token list, NULL terminated */ 258 (void *)&cmd_query_freq_token, 259 (void *)&cmd_query_freq_cpu_num_token, 260 NULL, 261 }, 262 }; 263 264 struct cmd_query_caps_result { 265 cmdline_fixed_string_t query_caps; 266 cmdline_fixed_string_t cpu_num; 267 }; 268 269 static int 270 receive_capabilities(struct channel_packet_caps_list *pkt_caps_list, 271 unsigned int lcore_id) 272 { 273 int ret; 274 275 ret = rte_power_guest_channel_receive_msg(pkt_caps_list, 276 sizeof(struct channel_packet_caps_list), 277 lcore_id); 278 if (ret < 0) { 279 RTE_LOG(ERR, GUEST_CLI, "Error receiving message.\n"); 280 return -1; 281 } 282 if (pkt_caps_list->command != CPU_POWER_CAPS_LIST) { 283 RTE_LOG(ERR, GUEST_CLI, "Unexpected message received.\n"); 284 return -1; 285 } 286 return 0; 287 } 288 289 static void 290 cmd_query_caps_list_parsed(void *parsed_result, 291 __rte_unused struct cmdline *cl, 292 __rte_unused void *data) 293 { 294 struct cmd_query_caps_result *res = parsed_result; 295 unsigned int lcore_id; 296 struct channel_packet_caps_list pkt_caps_list; 297 struct channel_packet pkt; 298 bool query_list = false; 299 int ret; 300 char *ep; 301 302 memset(&pkt, 0, sizeof(struct channel_packet)); 303 memset(&pkt_caps_list, 0, sizeof(struct channel_packet_caps_list)); 304 305 if (!strcmp(res->cpu_num, "all")) { 306 307 /* Get first enabled lcore. */ 308 lcore_id = rte_get_next_lcore(-1, 309 0, 310 0); 311 if (lcore_id == RTE_MAX_LCORE) { 312 cmdline_printf(cl, "Enabled core not found.\n"); 313 return; 314 } 315 316 pkt.command = CPU_POWER_QUERY_CAPS_LIST; 317 strlcpy(pkt.vm_name, policy.vm_name, sizeof(pkt.vm_name)); 318 query_list = true; 319 } else { 320 errno = 0; 321 lcore_id = (unsigned int)strtol(res->cpu_num, &ep, 10); 322 if (errno != 0 || lcore_id >= MAX_VCPU_PER_VM || 323 ep == res->cpu_num) { 324 cmdline_printf(cl, "Invalid parameter provided.\n"); 325 return; 326 } 327 pkt.command = CPU_POWER_QUERY_CAPS; 328 strlcpy(pkt.vm_name, policy.vm_name, sizeof(pkt.vm_name)); 329 pkt.resource_id = lcore_id; 330 } 331 332 ret = query_data(&pkt, lcore_id); 333 if (ret < 0) { 334 cmdline_printf(cl, "Error during sending capabilities query.\n"); 335 return; 336 } 337 338 ret = receive_capabilities(&pkt_caps_list, lcore_id); 339 if (ret < 0) { 340 cmdline_printf(cl, "Error during capabilities reception.\n"); 341 return; 342 } 343 if (query_list) { 344 unsigned int i; 345 for (i = 0; i < pkt_caps_list.num_vcpu; ++i) 346 cmdline_printf(cl, "Capabilities of [%d] vcore are:" 347 " turbo possibility: %" PRId64 ", " 348 "is priority core: %" PRId64 ".\n", 349 i, 350 pkt_caps_list.turbo[i], 351 pkt_caps_list.priority[i]); 352 } else { 353 cmdline_printf(cl, "Capabilities of [%d] vcore are:" 354 " turbo possibility: %" PRId64 ", " 355 "is priority core: %" PRId64 ".\n", 356 lcore_id, 357 pkt_caps_list.turbo[lcore_id], 358 pkt_caps_list.priority[lcore_id]); 359 } 360 } 361 362 cmdline_parse_token_string_t cmd_query_caps_token = 363 TOKEN_STRING_INITIALIZER(struct cmd_query_caps_result, query_caps, "query_cpu_caps"); 364 cmdline_parse_token_string_t cmd_query_caps_cpu_num_token = 365 TOKEN_STRING_INITIALIZER(struct cmd_query_caps_result, cpu_num, NULL); 366 367 cmdline_parse_inst_t cmd_query_caps_list = { 368 .f = cmd_query_caps_list_parsed, /* function to call */ 369 .data = NULL, /* 2nd arg of func */ 370 .help_str = "query_cpu_caps <core_num>|all, request" 371 " information regarding virtual core capabilities." 372 " The keyword 'all' will query list of all vcores for the VM", 373 .tokens = { /* token list, NULL terminated */ 374 (void *)&cmd_query_caps_token, 375 (void *)&cmd_query_caps_cpu_num_token, 376 NULL, 377 }, 378 }; 379 380 static int 381 check_response_cmd(unsigned int lcore_id, int *result) 382 { 383 struct channel_packet pkt; 384 int ret; 385 386 ret = rte_power_guest_channel_receive_msg(&pkt, sizeof pkt, lcore_id); 387 if (ret < 0) 388 return -1; 389 390 switch (pkt.command) { 391 case(CPU_POWER_CMD_ACK): 392 *result = 1; 393 break; 394 case(CPU_POWER_CMD_NACK): 395 *result = 0; 396 break; 397 default: 398 RTE_LOG(ERR, GUEST_CLI, 399 "Received invalid response from host, expecting ACK/NACK.\n"); 400 return -1; 401 } 402 403 return 0; 404 } 405 406 struct cmd_set_cpu_freq_result { 407 cmdline_fixed_string_t set_cpu_freq; 408 uint8_t lcore_id; 409 cmdline_fixed_string_t cmd; 410 }; 411 412 static void 413 cmd_set_cpu_freq_parsed(void *parsed_result, struct cmdline *cl, 414 __rte_unused void *data) 415 { 416 int ret = -1; 417 struct cmd_set_cpu_freq_result *res = parsed_result; 418 419 if (!strcmp(res->cmd, "up")) 420 ret = rte_power_freq_up(res->lcore_id); 421 else if (!strcmp(res->cmd, "down")) 422 ret = rte_power_freq_down(res->lcore_id); 423 else if (!strcmp(res->cmd, "min")) 424 ret = rte_power_freq_min(res->lcore_id); 425 else if (!strcmp(res->cmd, "max")) 426 ret = rte_power_freq_max(res->lcore_id); 427 else if (!strcmp(res->cmd, "enable_turbo")) 428 ret = rte_power_freq_enable_turbo(res->lcore_id); 429 else if (!strcmp(res->cmd, "disable_turbo")) 430 ret = rte_power_freq_disable_turbo(res->lcore_id); 431 432 if (ret != 1) { 433 cmdline_printf(cl, "Error sending message: %s\n", strerror(ret)); 434 return; 435 } 436 int result; 437 ret = check_response_cmd(res->lcore_id, &result); 438 if (ret < 0) { 439 RTE_LOG(ERR, GUEST_CLI, "No confirmation for sent message received\n"); 440 } else { 441 cmdline_printf(cl, "%s received for message sent to host.\n", 442 result == 1 ? "ACK" : "NACK"); 443 } 444 } 445 446 cmdline_parse_token_string_t cmd_set_cpu_freq = 447 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 448 set_cpu_freq, "set_cpu_freq"); 449 cmdline_parse_token_num_t cmd_set_cpu_freq_core_num = 450 TOKEN_NUM_INITIALIZER(struct cmd_set_cpu_freq_result, 451 lcore_id, RTE_UINT8); 452 cmdline_parse_token_string_t cmd_set_cpu_freq_cmd_cmd = 453 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 454 cmd, "up#down#min#max#enable_turbo#disable_turbo"); 455 456 cmdline_parse_inst_t cmd_set_cpu_freq_set = { 457 .f = cmd_set_cpu_freq_parsed, 458 .data = NULL, 459 .help_str = "set_cpu_freq <core_num> " 460 "<up|down|min|max|enable_turbo|disable_turbo>, " 461 "adjust the frequency for the specified core.", 462 .tokens = { 463 (void *)&cmd_set_cpu_freq, 464 (void *)&cmd_set_cpu_freq_core_num, 465 (void *)&cmd_set_cpu_freq_cmd_cmd, 466 NULL, 467 }, 468 }; 469 470 struct cmd_send_policy_result { 471 cmdline_fixed_string_t send_policy; 472 cmdline_fixed_string_t cmd; 473 }; 474 475 static inline int 476 send_policy(struct channel_packet *pkt, struct cmdline *cl) 477 { 478 int ret; 479 480 ret = rte_power_guest_channel_send_msg(pkt, 1); 481 if (ret < 0) { 482 RTE_LOG(ERR, GUEST_CLI, "Error sending message: %s\n", 483 ret > 0 ? strerror(ret) : "channel not connected"); 484 return -1; 485 } 486 487 int result; 488 ret = check_response_cmd(1, &result); 489 if (ret < 0) { 490 RTE_LOG(ERR, GUEST_CLI, "No confirmation for sent policy received\n"); 491 } else { 492 cmdline_printf(cl, "%s for sent policy received.\n", 493 result == 1 ? "ACK" : "NACK"); 494 } 495 return 1; 496 } 497 498 static void 499 cmd_send_policy_parsed(void *parsed_result, struct cmdline *cl, 500 __rte_unused void *data) 501 { 502 int ret = -1; 503 struct cmd_send_policy_result *res = parsed_result; 504 505 if (!strcmp(res->cmd, "now")) { 506 printf("Sending Policy down now!\n"); 507 ret = send_policy(&policy, cl); 508 } 509 if (ret != 1) 510 cmdline_printf(cl, "Error sending message: %s\n", 511 strerror(ret)); 512 } 513 514 cmdline_parse_token_string_t cmd_send_policy = 515 TOKEN_STRING_INITIALIZER(struct cmd_send_policy_result, 516 send_policy, "send_policy"); 517 cmdline_parse_token_string_t cmd_send_policy_cmd_cmd = 518 TOKEN_STRING_INITIALIZER(struct cmd_send_policy_result, 519 cmd, "now"); 520 521 cmdline_parse_inst_t cmd_send_policy_set = { 522 .f = cmd_send_policy_parsed, 523 .data = NULL, 524 .help_str = "send_policy now", 525 .tokens = { 526 (void *)&cmd_send_policy, 527 (void *)&cmd_send_policy_cmd_cmd, 528 NULL, 529 }, 530 }; 531 532 cmdline_parse_ctx_t main_ctx[] = { 533 (cmdline_parse_inst_t *)&cmd_quit, 534 (cmdline_parse_inst_t *)&cmd_send_policy_set, 535 (cmdline_parse_inst_t *)&cmd_set_cpu_freq_set, 536 (cmdline_parse_inst_t *)&cmd_query_freq_list, 537 (cmdline_parse_inst_t *)&cmd_query_caps_list, 538 NULL, 539 }; 540 541 void 542 run_cli(__rte_unused void *arg) 543 { 544 struct cmdline *cl; 545 546 cl = cmdline_stdin_new(main_ctx, "vmpower(guest)> "); 547 if (cl == NULL) 548 return; 549 550 cmdline_interact(cl); 551 cmdline_stdin_exit(cl); 552 } 553