xref: /dpdk/examples/vm_power_manager/guest_cli/vm_power_cli_guest.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
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