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