0bf5774ffc4b327fb63b89657d83898681d83518
[dpdk.git] / examples / vm_power_manager / guest_cli / vm_power_cli_guest.c
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 }