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