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