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