xref: /dpdk/examples/vm_power_manager/vm_power_cli.c (revision 72c00ae9dba7fc3f8c892b0354b956e885a25770)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <inttypes.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <termios.h>
11 #include <errno.h>
12 
13 #include <cmdline_rdline.h>
14 #include <cmdline_parse.h>
15 #include <cmdline_parse_string.h>
16 #include <cmdline_parse_num.h>
17 #include <cmdline_socket.h>
18 #include <cmdline.h>
19 
20 #include "vm_power_cli.h"
21 #include "channel_manager.h"
22 #include "channel_monitor.h"
23 #include "power_manager.h"
24 
25 struct cmd_quit_result {
26 	cmdline_fixed_string_t quit;
27 };
28 
29 static void cmd_quit_parsed(__rte_unused void *parsed_result,
30 		struct cmdline *cl,
31 		__rte_unused void *data)
32 {
33 	channel_monitor_exit();
34 	channel_manager_exit();
35 	power_manager_exit();
36 	cmdline_quit(cl);
37 }
38 
39 cmdline_parse_token_string_t cmd_quit_quit =
40 	TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
41 
42 cmdline_parse_inst_t cmd_quit = {
43 	.f = cmd_quit_parsed,  /* function to call */
44 	.data = NULL,      /* 2nd arg of func */
45 	.help_str = "close the application",
46 	.tokens = {        /* token list, NULL terminated */
47 		(void *)&cmd_quit_quit,
48 		NULL,
49 	},
50 };
51 
52 /* *** VM operations *** */
53 struct cmd_show_vm_result {
54 	cmdline_fixed_string_t show_vm;
55 	cmdline_fixed_string_t vm_name;
56 };
57 
58 static void
59 cmd_show_vm_parsed(void *parsed_result, struct cmdline *cl,
60 		__rte_unused void *data)
61 {
62 	struct cmd_show_vm_result *res = parsed_result;
63 	struct vm_info info;
64 	unsigned i;
65 
66 	if (get_info_vm(res->vm_name, &info) != 0)
67 		return;
68 	cmdline_printf(cl, "VM: '%s', status = ", info.name);
69 	if (info.status == CHANNEL_MGR_VM_ACTIVE)
70 		cmdline_printf(cl, "ACTIVE\n");
71 	else
72 		cmdline_printf(cl, "INACTIVE\n");
73 	cmdline_printf(cl, "Channels %u\n", info.num_channels);
74 	for (i = 0; i < info.num_channels; i++) {
75 		cmdline_printf(cl, "  [%u]: %s, status = ", i,
76 				info.channels[i].channel_path);
77 		switch (info.channels[i].status) {
78 		case CHANNEL_MGR_CHANNEL_CONNECTED:
79 			cmdline_printf(cl, "CONNECTED\n");
80 			break;
81 		case CHANNEL_MGR_CHANNEL_DISCONNECTED:
82 			cmdline_printf(cl, "DISCONNECTED\n");
83 			break;
84 		case CHANNEL_MGR_CHANNEL_DISABLED:
85 			cmdline_printf(cl, "DISABLED\n");
86 			break;
87 		case CHANNEL_MGR_CHANNEL_PROCESSING:
88 			cmdline_printf(cl, "PROCESSING\n");
89 			break;
90 		default:
91 			cmdline_printf(cl, "UNKNOWN\n");
92 			break;
93 		}
94 	}
95 	cmdline_printf(cl, "Virtual CPU(s): %u\n", info.num_vcpus);
96 	for (i = 0; i < info.num_vcpus; i++) {
97 		cmdline_printf(cl, "  [%u]: Physical CPU %d\n", i,
98 				info.pcpu_map[i]);
99 	}
100 }
101 
102 
103 
104 cmdline_parse_token_string_t cmd_vm_show =
105 	TOKEN_STRING_INITIALIZER(struct cmd_show_vm_result,
106 				show_vm, "show_vm");
107 cmdline_parse_token_string_t cmd_show_vm_name =
108 	TOKEN_STRING_INITIALIZER(struct cmd_show_vm_result,
109 			vm_name, NULL);
110 
111 cmdline_parse_inst_t cmd_show_vm_set = {
112 	.f = cmd_show_vm_parsed,
113 	.data = NULL,
114 	.help_str = "show_vm <vm_name>, prints the information on the "
115 			"specified VM(s), the information lists the number of vCPUS, the "
116 			"pinning to pCPU(s) as a bit mask, along with any communication "
117 			"channels associated with each VM",
118 	.tokens = {
119 		(void *)&cmd_vm_show,
120 		(void *)&cmd_show_vm_name,
121 		NULL,
122 	},
123 };
124 
125 /* *** vCPU to pCPU mapping operations *** */
126 
127 
128 struct cmd_set_pcpu_result {
129 	cmdline_fixed_string_t set_pcpu;
130 	cmdline_fixed_string_t vm_name;
131 	uint8_t vcpu;
132 	uint8_t core;
133 };
134 
135 static void
136 cmd_set_pcpu_parsed(void *parsed_result, struct cmdline *cl,
137 		__rte_unused void *data)
138 {
139 	struct cmd_set_pcpu_result *res = parsed_result;
140 
141 	if (set_pcpu(res->vm_name, res->vcpu, res->core) == 0)
142 		cmdline_printf(cl, "Pinned vCPU(%"PRId8") to pCPU core "
143 				"%"PRId8")\n", res->vcpu, res->core);
144 	else
145 		cmdline_printf(cl, "Unable to pin vCPU(%"PRId8") to pCPU core "
146 				"%"PRId8")\n", res->vcpu, res->core);
147 }
148 
149 cmdline_parse_token_string_t cmd_set_pcpu =
150 		TOKEN_STRING_INITIALIZER(struct cmd_set_pcpu_result,
151 				set_pcpu, "set_pcpu");
152 cmdline_parse_token_string_t cmd_set_pcpu_vm_name =
153 		TOKEN_STRING_INITIALIZER(struct cmd_set_pcpu_result,
154 				vm_name, NULL);
155 cmdline_parse_token_num_t set_pcpu_vcpu =
156 		TOKEN_NUM_INITIALIZER(struct cmd_set_pcpu_result,
157 				vcpu, RTE_UINT8);
158 cmdline_parse_token_num_t set_pcpu_core =
159 		TOKEN_NUM_INITIALIZER(struct cmd_set_pcpu_result,
160 				core, RTE_UINT64);
161 
162 
163 cmdline_parse_inst_t cmd_set_pcpu_set = {
164 		.f = cmd_set_pcpu_parsed,
165 		.data = NULL,
166 		.help_str = "set_pcpu <vm_name> <vcpu> <pcpu>, Set the binding "
167 				"of Virtual CPU on VM to the Physical CPU.",
168 				.tokens = {
169 						(void *)&cmd_set_pcpu,
170 						(void *)&cmd_set_pcpu_vm_name,
171 						(void *)&set_pcpu_vcpu,
172 						(void *)&set_pcpu_core,
173 						NULL,
174 		},
175 };
176 
177 struct cmd_vm_op_result {
178 	cmdline_fixed_string_t op_vm;
179 	cmdline_fixed_string_t vm_name;
180 };
181 
182 static void
183 cmd_vm_op_parsed(void *parsed_result, struct cmdline *cl,
184 		__rte_unused void *data)
185 {
186 	struct cmd_vm_op_result *res = parsed_result;
187 
188 	if (!strcmp(res->op_vm, "add_vm")) {
189 		if (add_vm(res->vm_name) < 0)
190 			cmdline_printf(cl, "Unable to add VM '%s'\n", res->vm_name);
191 	} else if (remove_vm(res->vm_name) < 0)
192 		cmdline_printf(cl, "Unable to remove VM '%s'\n", res->vm_name);
193 }
194 
195 cmdline_parse_token_string_t cmd_vm_op =
196 	TOKEN_STRING_INITIALIZER(struct cmd_vm_op_result,
197 			op_vm, "add_vm#rm_vm");
198 cmdline_parse_token_string_t cmd_vm_name =
199 	TOKEN_STRING_INITIALIZER(struct cmd_vm_op_result,
200 			vm_name, NULL);
201 
202 cmdline_parse_inst_t cmd_vm_op_set = {
203 	.f = cmd_vm_op_parsed,
204 	.data = NULL,
205 	.help_str = "add_vm|rm_vm <name>, add a VM for "
206 			"subsequent operations with the CLI or remove a previously added "
207 			"VM from the VM Power Manager",
208 	.tokens = {
209 		(void *)&cmd_vm_op,
210 		(void *)&cmd_vm_name,
211 	NULL,
212 	},
213 };
214 
215 /* *** VM channel operations *** */
216 struct cmd_channels_op_result {
217 	cmdline_fixed_string_t op;
218 	cmdline_fixed_string_t vm_name;
219 	cmdline_fixed_string_t channel_list;
220 };
221 static void
222 cmd_channels_op_parsed(void *parsed_result, struct cmdline *cl,
223 			__rte_unused void *data)
224 {
225 	unsigned num_channels = 0, channel_num, i;
226 	int channels_added;
227 	unsigned int channel_list[RTE_MAX_LCORE];
228 	char *token, *remaining, *tail_ptr;
229 	struct cmd_channels_op_result *res = parsed_result;
230 
231 	if (!strcmp(res->channel_list, "all")) {
232 		channels_added = add_all_channels(res->vm_name);
233 		cmdline_printf(cl, "Added %d channels for VM '%s'\n",
234 				channels_added, res->vm_name);
235 		return;
236 	}
237 
238 	remaining = res->channel_list;
239 	while (1) {
240 		if (remaining == NULL || remaining[0] == '\0')
241 			break;
242 
243 		token = strsep(&remaining, ",");
244 		if (token == NULL)
245 			break;
246 		errno = 0;
247 		channel_num = (unsigned)strtol(token, &tail_ptr, 10);
248 		if ((errno != 0) || tail_ptr == NULL || (*tail_ptr != '\0'))
249 			break;
250 
251 		if (channel_num == RTE_MAX_LCORE) {
252 			cmdline_printf(cl, "Channel number '%u' exceeds the maximum number "
253 					"of allowable channels(%u) for VM '%s'\n", channel_num,
254 					RTE_MAX_LCORE, res->vm_name);
255 			return;
256 		}
257 		channel_list[num_channels++] = channel_num;
258 	}
259 	for (i = 0; i < num_channels; i++)
260 		cmdline_printf(cl, "[%u]: Adding channel %u\n", i, channel_list[i]);
261 
262 	channels_added = add_channels(res->vm_name, channel_list,
263 			num_channels);
264 	cmdline_printf(cl, "Enabled %d channels for '%s'\n", channels_added,
265 			res->vm_name);
266 }
267 
268 cmdline_parse_token_string_t cmd_channels_op =
269 	TOKEN_STRING_INITIALIZER(struct cmd_channels_op_result,
270 				op, "add_channels");
271 cmdline_parse_token_string_t cmd_channels_vm_name =
272 	TOKEN_STRING_INITIALIZER(struct cmd_channels_op_result,
273 			vm_name, NULL);
274 cmdline_parse_token_string_t cmd_channels_list =
275 	TOKEN_STRING_INITIALIZER(struct cmd_channels_op_result,
276 			channel_list, NULL);
277 
278 cmdline_parse_inst_t cmd_channels_op_set = {
279 	.f = cmd_channels_op_parsed,
280 	.data = NULL,
281 	.help_str = "add_channels <vm_name> <list>|all, add "
282 			"communication channels for the specified VM, the "
283 			"virtio channels must be enabled in the VM "
284 			"configuration(qemu/libvirt) and the associated VM must be active. "
285 			"<list> is a comma-separated list of channel numbers to add, using "
286 			"the keyword 'all' will attempt to add all channels for the VM",
287 	.tokens = {
288 		(void *)&cmd_channels_op,
289 		(void *)&cmd_channels_vm_name,
290 		(void *)&cmd_channels_list,
291 		NULL,
292 	},
293 };
294 
295 struct cmd_set_query_result {
296 	cmdline_fixed_string_t set_query;
297 	cmdline_fixed_string_t vm_name;
298 	cmdline_fixed_string_t query_status;
299 };
300 
301 static void
302 cmd_set_query_parsed(void *parsed_result,
303 		__rte_unused struct cmdline *cl,
304 		__rte_unused void *data)
305 {
306 	struct cmd_set_query_result *res = parsed_result;
307 
308 	if (!strcmp(res->query_status, "enable")) {
309 		if (set_query_status(res->vm_name, true) < 0)
310 			cmdline_printf(cl, "Unable to allow query for VM '%s'\n",
311 					res->vm_name);
312 	} else if (!strcmp(res->query_status, "disable")) {
313 		if (set_query_status(res->vm_name, false) < 0)
314 			cmdline_printf(cl, "Unable to disallow query for VM '%s'\n",
315 					res->vm_name);
316 	}
317 }
318 
319 cmdline_parse_token_string_t cmd_set_query =
320 	TOKEN_STRING_INITIALIZER(struct cmd_set_query_result,
321 			set_query, "set_query");
322 cmdline_parse_token_string_t cmd_set_query_vm_name =
323 	TOKEN_STRING_INITIALIZER(struct cmd_set_query_result,
324 			vm_name, NULL);
325 cmdline_parse_token_string_t cmd_set_query_status =
326 	TOKEN_STRING_INITIALIZER(struct cmd_set_query_result,
327 			query_status, "enable#disable");
328 
329 cmdline_parse_inst_t cmd_set_query_set = {
330 	.f = cmd_set_query_parsed,
331 	.data = NULL,
332 	.help_str = "set_query <vm_name> <enable|disable>, allow or disallow queries"
333 			" for the specified VM",
334 	.tokens = {
335 		(void *)&cmd_set_query,
336 		(void *)&cmd_set_query_vm_name,
337 		(void *)&cmd_set_query_status,
338 		NULL,
339 	},
340 };
341 
342 struct cmd_channels_status_op_result {
343 	cmdline_fixed_string_t op;
344 	cmdline_fixed_string_t vm_name;
345 	cmdline_fixed_string_t channel_list;
346 	cmdline_fixed_string_t status;
347 };
348 
349 static void
350 cmd_channels_status_op_parsed(void *parsed_result, struct cmdline *cl,
351 		       __rte_unused void *data)
352 {
353 	unsigned num_channels = 0, channel_num;
354 	int changed;
355 	unsigned int channel_list[RTE_MAX_LCORE];
356 	char *token, *remaining, *tail_ptr;
357 	struct cmd_channels_status_op_result *res = parsed_result;
358 	enum channel_status status;
359 
360 	if (!strcmp(res->status, "enabled"))
361 		status = CHANNEL_MGR_CHANNEL_CONNECTED;
362 	else
363 		status = CHANNEL_MGR_CHANNEL_DISABLED;
364 
365 	if (!strcmp(res->channel_list, "all")) {
366 		changed = set_channel_status_all(res->vm_name, status);
367 		cmdline_printf(cl, "Updated status of %d channels "
368 				"for VM '%s'\n", changed, res->vm_name);
369 		return;
370 	}
371 	remaining = res->channel_list;
372 	while (1) {
373 		if (remaining == NULL || remaining[0] == '\0')
374 			break;
375 		token = strsep(&remaining, ",");
376 		if (token == NULL)
377 			break;
378 		errno = 0;
379 		channel_num = (unsigned)strtol(token, &tail_ptr, 10);
380 		if ((errno != 0) || tail_ptr == NULL || (*tail_ptr != '\0'))
381 			break;
382 
383 		if (channel_num == RTE_MAX_LCORE) {
384 			cmdline_printf(cl, "%u exceeds the maximum number of allowable "
385 					"channels(%u) for VM '%s'\n", channel_num,
386 					RTE_MAX_LCORE, res->vm_name);
387 			return;
388 		}
389 		channel_list[num_channels++] = channel_num;
390 	}
391 	changed = set_channel_status(res->vm_name, channel_list, num_channels,
392 			status);
393 	cmdline_printf(cl, "Updated status of %d channels "
394 					"for VM '%s'\n", changed, res->vm_name);
395 }
396 
397 cmdline_parse_token_string_t cmd_channels_status_op =
398 	TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result,
399 				op, "set_channel_status");
400 cmdline_parse_token_string_t cmd_channels_status_vm_name =
401 	TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result,
402 			vm_name, NULL);
403 cmdline_parse_token_string_t cmd_channels_status_list =
404 	TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result,
405 			channel_list, NULL);
406 cmdline_parse_token_string_t cmd_channels_status =
407 	TOKEN_STRING_INITIALIZER(struct cmd_channels_status_op_result,
408 			status, "enabled#disabled");
409 
410 cmdline_parse_inst_t cmd_channels_status_op_set = {
411 	.f = cmd_channels_status_op_parsed,
412 	.data = NULL,
413 	.help_str = "set_channel_status <vm_name> <list>|all enabled|disabled, "
414 			" enable or disable the communication channels in "
415 			"list(comma-separated) for the specified VM, alternatively "
416 			"list can be replaced with keyword 'all'. "
417 			"Disabled channels will still receive packets on the host, "
418 			"however the commands they specify will be ignored. "
419 			"Set status to 'enabled' to begin processing requests again.",
420 	.tokens = {
421 		(void *)&cmd_channels_status_op,
422 		(void *)&cmd_channels_status_vm_name,
423 		(void *)&cmd_channels_status_list,
424 		(void *)&cmd_channels_status,
425 		NULL,
426 	},
427 };
428 
429 /* *** CPU Frequency operations *** */
430 struct cmd_show_cpu_freq_result {
431 	cmdline_fixed_string_t show_cpu_freq;
432 	uint8_t core_num;
433 };
434 
435 static void
436 cmd_show_cpu_freq_parsed(void *parsed_result, struct cmdline *cl,
437 		       __rte_unused void *data)
438 {
439 	struct cmd_show_cpu_freq_result *res = parsed_result;
440 	uint32_t curr_freq = power_manager_get_current_frequency(res->core_num);
441 
442 	if (curr_freq == 0) {
443 		cmdline_printf(cl, "Unable to get frequency for core %u\n",
444 				res->core_num);
445 		return;
446 	}
447 	cmdline_printf(cl, "Core %u frequency: %"PRId32"\n", res->core_num,
448 			curr_freq);
449 }
450 
451 cmdline_parse_token_string_t cmd_show_cpu_freq =
452 	TOKEN_STRING_INITIALIZER(struct cmd_show_cpu_freq_result,
453 			show_cpu_freq, "show_cpu_freq");
454 
455 cmdline_parse_token_num_t cmd_show_cpu_freq_core_num =
456 	TOKEN_NUM_INITIALIZER(struct cmd_show_cpu_freq_result,
457 			core_num, RTE_UINT8);
458 
459 cmdline_parse_inst_t cmd_show_cpu_freq_set = {
460 	.f = cmd_show_cpu_freq_parsed,
461 	.data = NULL,
462 	.help_str = "Get the current frequency for the specified core",
463 	.tokens = {
464 		(void *)&cmd_show_cpu_freq,
465 		(void *)&cmd_show_cpu_freq_core_num,
466 		NULL,
467 	},
468 };
469 
470 struct cmd_set_cpu_freq_result {
471 	cmdline_fixed_string_t set_cpu_freq;
472 	uint8_t core_num;
473 	cmdline_fixed_string_t cmd;
474 };
475 
476 static void
477 cmd_set_cpu_freq_parsed(void *parsed_result, struct cmdline *cl,
478 		       __rte_unused void *data)
479 {
480 	int ret = -1;
481 	struct cmd_set_cpu_freq_result *res = parsed_result;
482 
483 	if (!strcmp(res->cmd , "up"))
484 		ret = power_manager_scale_core_up(res->core_num);
485 	else if (!strcmp(res->cmd , "down"))
486 		ret = power_manager_scale_core_down(res->core_num);
487 	else if (!strcmp(res->cmd , "min"))
488 		ret = power_manager_scale_core_min(res->core_num);
489 	else if (!strcmp(res->cmd , "max"))
490 		ret = power_manager_scale_core_max(res->core_num);
491 	else if (!strcmp(res->cmd, "enable_turbo"))
492 		ret = power_manager_enable_turbo_core(res->core_num);
493 	else if (!strcmp(res->cmd, "disable_turbo"))
494 		ret = power_manager_disable_turbo_core(res->core_num);
495 	if (ret < 0) {
496 		cmdline_printf(cl, "Error scaling core(%u) '%s'\n", res->core_num,
497 				res->cmd);
498 	}
499 }
500 
501 cmdline_parse_token_string_t cmd_set_cpu_freq =
502 	TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result,
503 			set_cpu_freq, "set_cpu_freq");
504 cmdline_parse_token_num_t cmd_set_cpu_freq_core_num =
505 	TOKEN_NUM_INITIALIZER(struct cmd_set_cpu_freq_result,
506 			core_num, RTE_UINT8);
507 cmdline_parse_token_string_t cmd_set_cpu_freq_cmd_cmd =
508 	TOKEN_STRING_INITIALIZER(struct cmd_set_cpu_freq_result,
509 			cmd, "up#down#min#max#enable_turbo#disable_turbo");
510 
511 cmdline_parse_inst_t cmd_set_cpu_freq_set = {
512 	.f = cmd_set_cpu_freq_parsed,
513 	.data = NULL,
514 	.help_str = "set_cpu_freq <core_num> <up|down|min|max|enable_turbo|disable_turbo>, adjust the current "
515 			"frequency for the specified core",
516 	.tokens = {
517 		(void *)&cmd_set_cpu_freq,
518 		(void *)&cmd_set_cpu_freq_core_num,
519 		(void *)&cmd_set_cpu_freq_cmd_cmd,
520 		NULL,
521 	},
522 };
523 
524 cmdline_parse_ctx_t main_ctx[] = {
525 		(cmdline_parse_inst_t *)&cmd_quit,
526 		(cmdline_parse_inst_t *)&cmd_vm_op_set,
527 		(cmdline_parse_inst_t *)&cmd_channels_op_set,
528 		(cmdline_parse_inst_t *)&cmd_channels_status_op_set,
529 		(cmdline_parse_inst_t *)&cmd_show_vm_set,
530 		(cmdline_parse_inst_t *)&cmd_show_cpu_freq_set,
531 		(cmdline_parse_inst_t *)&cmd_set_cpu_freq_set,
532 		(cmdline_parse_inst_t *)&cmd_set_pcpu_set,
533 		(cmdline_parse_inst_t *)&cmd_set_query_set,
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> ");
543 	if (cl == NULL)
544 		return;
545 
546 	cmdline_interact(cl);
547 	cmdline_stdin_exit(cl);
548 }
549