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