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: %ld, is priority core: %ld.\n", 348 i, 349 pkt_caps_list.turbo[i], 350 pkt_caps_list.priority[i]); 351 } else { 352 cmdline_printf(cl, "Capabilities of [%d] vcore are:" 353 " turbo possibility: %ld, is priority core: %ld.\n", 354 lcore_id, 355 pkt_caps_list.turbo[lcore_id], 356 pkt_caps_list.priority[lcore_id]); 357 } 358 } 359 360 cmdline_parse_token_string_t cmd_query_caps_token = 361 TOKEN_STRING_INITIALIZER(struct cmd_query_caps_result, query_caps, "query_cpu_caps"); 362 cmdline_parse_token_string_t cmd_query_caps_cpu_num_token = 363 TOKEN_STRING_INITIALIZER(struct cmd_query_caps_result, cpu_num, NULL); 364 365 cmdline_parse_inst_t cmd_query_caps_list = { 366 .f = cmd_query_caps_list_parsed, /* function to call */ 367 .data = NULL, /* 2nd arg of func */ 368 .help_str = "query_cpu_caps <core_num>|all, request" 369 " information regarding virtual core capabilities." 370 " The keyword 'all' will query list of all vcores for the VM", 371 .tokens = { /* token list, NULL terminated */ 372 (void *)&cmd_query_caps_token, 373 (void *)&cmd_query_caps_cpu_num_token, 374 NULL, 375 }, 376 }; 377 378 static int 379 check_response_cmd(unsigned int lcore_id, int *result) 380 { 381 struct channel_packet pkt; 382 int ret; 383 384 ret = rte_power_guest_channel_receive_msg(&pkt, sizeof pkt, lcore_id); 385 if (ret < 0) 386 return -1; 387 388 switch (pkt.command) { 389 case(CPU_POWER_CMD_ACK): 390 *result = 1; 391 break; 392 case(CPU_POWER_CMD_NACK): 393 *result = 0; 394 break; 395 default: 396 RTE_LOG(ERR, GUEST_CLI, 397 "Received invalid response from host, expecting ACK/NACK.\n"); 398 return -1; 399 } 400 401 return 0; 402 } 403 404 struct cmd_set_cpu_freq_result { 405 cmdline_fixed_string_t set_cpu_freq; 406 uint8_t lcore_id; 407 cmdline_fixed_string_t cmd; 408 }; 409 410 static void 411 cmd_set_cpu_freq_parsed(void *parsed_result, struct cmdline *cl, 412 __rte_unused void *data) 413 { 414 int ret = -1; 415 struct cmd_set_cpu_freq_result *res = parsed_result; 416 417 if (!strcmp(res->cmd, "up")) 418 ret = rte_power_freq_up(res->lcore_id); 419 else if (!strcmp(res->cmd, "down")) 420 ret = rte_power_freq_down(res->lcore_id); 421 else if (!strcmp(res->cmd, "min")) 422 ret = rte_power_freq_min(res->lcore_id); 423 else if (!strcmp(res->cmd, "max")) 424 ret = rte_power_freq_max(res->lcore_id); 425 else if (!strcmp(res->cmd, "enable_turbo")) 426 ret = rte_power_freq_enable_turbo(res->lcore_id); 427 else if (!strcmp(res->cmd, "disable_turbo")) 428 ret = rte_power_freq_disable_turbo(res->lcore_id); 429 430 if (ret != 1) { 431 cmdline_printf(cl, "Error sending message: %s\n", strerror(ret)); 432 return; 433 } 434 int result; 435 ret = check_response_cmd(res->lcore_id, &result); 436 if (ret < 0) { 437 RTE_LOG(ERR, GUEST_CLI, "No confirmation for sent message received\n"); 438 } else { 439 cmdline_printf(cl, "%s received for message sent to host.\n", 440 result == 1 ? "ACK" : "NACK"); 441 } 442 } 443 444 cmdline_parse_token_string_t cmd_set_cpu_freq = 445 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 446 set_cpu_freq, "set_cpu_freq"); 447 cmdline_parse_token_num_t cmd_set_cpu_freq_core_num = 448 TOKEN_NUM_INITIALIZER(struct cmd_set_cpu_freq_result, 449 lcore_id, UINT8); 450 cmdline_parse_token_string_t cmd_set_cpu_freq_cmd_cmd = 451 TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result, 452 cmd, "up#down#min#max#enable_turbo#disable_turbo"); 453 454 cmdline_parse_inst_t cmd_set_cpu_freq_set = { 455 .f = cmd_set_cpu_freq_parsed, 456 .data = NULL, 457 .help_str = "set_cpu_freq <core_num> " 458 "<up|down|min|max|enable_turbo|disable_turbo>, " 459 "adjust the frequency for the specified core.", 460 .tokens = { 461 (void *)&cmd_set_cpu_freq, 462 (void *)&cmd_set_cpu_freq_core_num, 463 (void *)&cmd_set_cpu_freq_cmd_cmd, 464 NULL, 465 }, 466 }; 467 468 struct cmd_send_policy_result { 469 cmdline_fixed_string_t send_policy; 470 cmdline_fixed_string_t cmd; 471 }; 472 473 static inline int 474 send_policy(struct channel_packet *pkt, struct cmdline *cl) 475 { 476 int ret; 477 478 ret = rte_power_guest_channel_send_msg(pkt, 1); 479 if (ret < 0) { 480 RTE_LOG(ERR, GUEST_CLI, "Error sending message: %s\n", 481 ret > 0 ? strerror(ret) : "channel not connected"); 482 return -1; 483 } 484 485 int result; 486 ret = check_response_cmd(1, &result); 487 if (ret < 0) { 488 RTE_LOG(ERR, GUEST_CLI, "No confirmation for sent policy received\n"); 489 } else { 490 cmdline_printf(cl, "%s for sent policy received.\n", 491 result == 1 ? "ACK" : "NACK"); 492 } 493 return 1; 494 } 495 496 static void 497 cmd_send_policy_parsed(void *parsed_result, struct cmdline *cl, 498 __rte_unused void *data) 499 { 500 int ret = -1; 501 struct cmd_send_policy_result *res = parsed_result; 502 503 if (!strcmp(res->cmd, "now")) { 504 printf("Sending Policy down now!\n"); 505 ret = send_policy(&policy, cl); 506 } 507 if (ret != 1) 508 cmdline_printf(cl, "Error sending message: %s\n", 509 strerror(ret)); 510 } 511 512 cmdline_parse_token_string_t cmd_send_policy = 513 TOKEN_STRING_INITIALIZER(struct cmd_send_policy_result, 514 send_policy, "send_policy"); 515 cmdline_parse_token_string_t cmd_send_policy_cmd_cmd = 516 TOKEN_STRING_INITIALIZER(struct cmd_send_policy_result, 517 cmd, "now"); 518 519 cmdline_parse_inst_t cmd_send_policy_set = { 520 .f = cmd_send_policy_parsed, 521 .data = NULL, 522 .help_str = "send_policy now", 523 .tokens = { 524 (void *)&cmd_send_policy, 525 (void *)&cmd_send_policy_cmd_cmd, 526 NULL, 527 }, 528 }; 529 530 cmdline_parse_ctx_t main_ctx[] = { 531 (cmdline_parse_inst_t *)&cmd_quit, 532 (cmdline_parse_inst_t *)&cmd_send_policy_set, 533 (cmdline_parse_inst_t *)&cmd_set_cpu_freq_set, 534 (cmdline_parse_inst_t *)&cmd_query_freq_list, 535 (cmdline_parse_inst_t *)&cmd_query_caps_list, 536 NULL, 537 }; 538 539 void 540 run_cli(__rte_unused void *arg) 541 { 542 struct cmdline *cl; 543 544 cl = cmdline_stdin_new(main_ctx, "vmpower(guest)> "); 545 if (cl == NULL) 546 return; 547 548 cmdline_interact(cl); 549 cmdline_stdin_exit(cl); 550 } 551