New upstream version 18.11-rc1
[deb_dpdk.git] / examples / vm_power_manager / channel_manager.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/un.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <inttypes.h>
11 #include <dirent.h>
12 #include <errno.h>
13
14 #include <sys/queue.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/socket.h>
18 #include <sys/select.h>
19
20 #include <rte_malloc.h>
21 #include <rte_memory.h>
22 #include <rte_mempool.h>
23 #include <rte_log.h>
24 #include <rte_atomic.h>
25 #include <rte_spinlock.h>
26
27 #include <libvirt/libvirt.h>
28
29 #include "channel_manager.h"
30 #include "channel_commands.h"
31 #include "channel_monitor.h"
32
33
34 #define RTE_LOGTYPE_CHANNEL_MANAGER RTE_LOGTYPE_USER1
35
36 #define ITERATIVE_BITMASK_CHECK_64(mask_u64b, i) \
37                 for (i = 0; mask_u64b; mask_u64b &= ~(1ULL << i++)) \
38                 if ((mask_u64b >> i) & 1) \
39
40 /* Global pointer to libvirt connection */
41 static virConnectPtr global_vir_conn_ptr;
42
43 static unsigned char *global_cpumaps;
44 static virVcpuInfo *global_vircpuinfo;
45 static size_t global_maplen;
46
47 static unsigned int global_n_host_cpus;
48 static bool global_hypervisor_available;
49
50 /*
51  * Represents a single Virtual Machine
52  */
53 struct virtual_machine_info {
54         char name[CHANNEL_MGR_MAX_NAME_LEN];
55         rte_atomic64_t pcpu_mask[CHANNEL_CMDS_MAX_CPUS];
56         struct channel_info *channels[CHANNEL_CMDS_MAX_VM_CHANNELS];
57         uint64_t channel_mask;
58         uint8_t num_channels;
59         enum vm_status status;
60         virDomainPtr domainPtr;
61         virDomainInfo info;
62         rte_spinlock_t config_spinlock;
63         LIST_ENTRY(virtual_machine_info) vms_info;
64 };
65
66 LIST_HEAD(, virtual_machine_info) vm_list_head;
67
68 static struct virtual_machine_info *
69 find_domain_by_name(const char *name)
70 {
71         struct virtual_machine_info *info;
72         LIST_FOREACH(info, &vm_list_head, vms_info) {
73                 if (!strncmp(info->name, name, CHANNEL_MGR_MAX_NAME_LEN-1))
74                         return info;
75         }
76         return NULL;
77 }
78
79 static int
80 update_pcpus_mask(struct virtual_machine_info *vm_info)
81 {
82         virVcpuInfoPtr cpuinfo;
83         unsigned i, j;
84         int n_vcpus;
85         uint64_t mask;
86
87         memset(global_cpumaps, 0, CHANNEL_CMDS_MAX_CPUS*global_maplen);
88
89         if (!virDomainIsActive(vm_info->domainPtr)) {
90                 n_vcpus = virDomainGetVcpuPinInfo(vm_info->domainPtr,
91                                 vm_info->info.nrVirtCpu, global_cpumaps, global_maplen,
92                                 VIR_DOMAIN_AFFECT_CONFIG);
93                 if (n_vcpus < 0) {
94                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting vCPU info for "
95                                         "in-active VM '%s'\n", vm_info->name);
96                         return -1;
97                 }
98                 goto update_pcpus;
99         }
100
101         memset(global_vircpuinfo, 0, sizeof(*global_vircpuinfo)*
102                         CHANNEL_CMDS_MAX_CPUS);
103
104         cpuinfo = global_vircpuinfo;
105
106         n_vcpus = virDomainGetVcpus(vm_info->domainPtr, cpuinfo,
107                         CHANNEL_CMDS_MAX_CPUS, global_cpumaps, global_maplen);
108         if (n_vcpus < 0) {
109                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting vCPU info for "
110                                 "active VM '%s'\n", vm_info->name);
111                 return -1;
112         }
113 update_pcpus:
114         if (n_vcpus >= CHANNEL_CMDS_MAX_CPUS) {
115                 RTE_LOG(ERR, CHANNEL_MANAGER, "Number of vCPUS(%u) is out of range "
116                                 "0...%d\n", n_vcpus, CHANNEL_CMDS_MAX_CPUS-1);
117                 return -1;
118         }
119         if (n_vcpus != vm_info->info.nrVirtCpu) {
120                 RTE_LOG(INFO, CHANNEL_MANAGER, "Updating the number of vCPUs for VM '%s"
121                                 " from %d -> %d\n", vm_info->name, vm_info->info.nrVirtCpu,
122                                 n_vcpus);
123                 vm_info->info.nrVirtCpu = n_vcpus;
124         }
125         for (i = 0; i < vm_info->info.nrVirtCpu; i++) {
126                 mask = 0;
127                 for (j = 0; j < global_n_host_cpus; j++) {
128                         if (VIR_CPU_USABLE(global_cpumaps, global_maplen, i, j) > 0) {
129                                 mask |= 1ULL << j;
130                         }
131                 }
132                 rte_atomic64_set(&vm_info->pcpu_mask[i], mask);
133         }
134         return 0;
135 }
136
137 int
138 set_pcpus_mask(char *vm_name, unsigned vcpu, uint64_t core_mask)
139 {
140         unsigned i = 0;
141         int flags = VIR_DOMAIN_AFFECT_LIVE|VIR_DOMAIN_AFFECT_CONFIG;
142         struct virtual_machine_info *vm_info;
143         uint64_t mask = core_mask;
144
145         if (vcpu >= CHANNEL_CMDS_MAX_CPUS) {
146                 RTE_LOG(ERR, CHANNEL_MANAGER, "vCPU(%u) exceeds max allowable(%d)\n",
147                                 vcpu, CHANNEL_CMDS_MAX_CPUS-1);
148                 return -1;
149         }
150
151         vm_info = find_domain_by_name(vm_name);
152         if (vm_info == NULL) {
153                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM '%s' not found\n", vm_name);
154                 return -1;
155         }
156
157         if (!virDomainIsActive(vm_info->domainPtr)) {
158                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to set vCPU(%u) to pCPU "
159                                 "mask(0x%"PRIx64") for VM '%s', VM is not active\n",
160                                 vcpu, core_mask, vm_info->name);
161                 return -1;
162         }
163
164         if (vcpu >= vm_info->info.nrVirtCpu) {
165                 RTE_LOG(ERR, CHANNEL_MANAGER, "vCPU(%u) exceeds the assigned number of "
166                                 "vCPUs(%u)\n", vcpu, vm_info->info.nrVirtCpu);
167                 return -1;
168         }
169         memset(global_cpumaps, 0 , CHANNEL_CMDS_MAX_CPUS * global_maplen);
170         ITERATIVE_BITMASK_CHECK_64(mask, i) {
171                 VIR_USE_CPU(global_cpumaps, i);
172                 if (i >= global_n_host_cpus) {
173                         RTE_LOG(ERR, CHANNEL_MANAGER, "CPU(%u) exceeds the available "
174                                         "number of CPUs(%u)\n", i, global_n_host_cpus);
175                         return -1;
176                 }
177         }
178         if (virDomainPinVcpuFlags(vm_info->domainPtr, vcpu, global_cpumaps,
179                         global_maplen, flags) < 0) {
180                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to set vCPU(%u) to pCPU "
181                                 "mask(0x%"PRIx64") for VM '%s'\n", vcpu, core_mask,
182                                 vm_info->name);
183                 return -1;
184         }
185         rte_atomic64_set(&vm_info->pcpu_mask[vcpu], core_mask);
186         return 0;
187
188 }
189
190 int
191 set_pcpu(char *vm_name, unsigned vcpu, unsigned core_num)
192 {
193         uint64_t mask = 1ULL << core_num;
194
195         return set_pcpus_mask(vm_name, vcpu, mask);
196 }
197
198 uint64_t
199 get_pcpus_mask(struct channel_info *chan_info, unsigned vcpu)
200 {
201         struct virtual_machine_info *vm_info =
202                         (struct virtual_machine_info *)chan_info->priv_info;
203
204         if (global_hypervisor_available && (vm_info != NULL))
205                 return rte_atomic64_read(&vm_info->pcpu_mask[vcpu]);
206         else
207                 return 0;
208 }
209
210 static inline int
211 channel_exists(struct virtual_machine_info *vm_info, unsigned channel_num)
212 {
213         rte_spinlock_lock(&(vm_info->config_spinlock));
214         if (vm_info->channel_mask & (1ULL << channel_num)) {
215                 rte_spinlock_unlock(&(vm_info->config_spinlock));
216                 return 1;
217         }
218         rte_spinlock_unlock(&(vm_info->config_spinlock));
219         return 0;
220 }
221
222
223
224 static int
225 open_non_blocking_channel(struct channel_info *info)
226 {
227         int ret, flags;
228         struct sockaddr_un sock_addr;
229         fd_set soc_fd_set;
230         struct timeval tv;
231
232         info->fd = socket(AF_UNIX, SOCK_STREAM, 0);
233         if (info->fd == -1) {
234                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error(%s) creating socket for '%s'\n",
235                                 strerror(errno),
236                                 info->channel_path);
237                 return -1;
238         }
239         sock_addr.sun_family = AF_UNIX;
240         memcpy(&sock_addr.sun_path, info->channel_path,
241                         strlen(info->channel_path)+1);
242
243         /* Get current flags */
244         flags = fcntl(info->fd, F_GETFL, 0);
245         if (flags < 0) {
246                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) fcntl get flags socket for"
247                                 "'%s'\n", strerror(errno), info->channel_path);
248                 return 1;
249         }
250         /* Set to Non Blocking */
251         flags |= O_NONBLOCK;
252         if (fcntl(info->fd, F_SETFL, flags) < 0) {
253                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) setting non-blocking "
254                                 "socket for '%s'\n", strerror(errno), info->channel_path);
255                 return -1;
256         }
257         ret = connect(info->fd, (struct sockaddr *)&sock_addr,
258                         sizeof(sock_addr));
259         if (ret < 0) {
260                 /* ECONNREFUSED error is given when VM is not active */
261                 if (errno == ECONNREFUSED) {
262                         RTE_LOG(WARNING, CHANNEL_MANAGER, "VM is not active or has not "
263                                         "activated its endpoint to channel %s\n",
264                                         info->channel_path);
265                         return -1;
266                 }
267                 /* Wait for tv_sec if in progress */
268                 else if (errno == EINPROGRESS) {
269                         tv.tv_sec = 2;
270                         tv.tv_usec = 0;
271                         FD_ZERO(&soc_fd_set);
272                         FD_SET(info->fd, &soc_fd_set);
273                         if (select(info->fd+1, NULL, &soc_fd_set, NULL, &tv) > 0) {
274                                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Timeout or error on channel "
275                                                 "'%s'\n", info->channel_path);
276                                 return -1;
277                         }
278                 } else {
279                         /* Any other error */
280                         RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) connecting socket"
281                                         " for '%s'\n", strerror(errno), info->channel_path);
282                         return -1;
283                 }
284         }
285         return 0;
286 }
287
288 static int
289 open_host_channel(struct channel_info *info)
290 {
291         int flags;
292
293         info->fd = open(info->channel_path, O_RDWR | O_RSYNC);
294         if (info->fd == -1) {
295                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error(%s) opening fifo for '%s'\n",
296                                 strerror(errno),
297                                 info->channel_path);
298                 return -1;
299         }
300
301         /* Get current flags */
302         flags = fcntl(info->fd, F_GETFL, 0);
303         if (flags < 0) {
304                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) fcntl get flags socket for"
305                                 "'%s'\n", strerror(errno), info->channel_path);
306                 return 1;
307         }
308         /* Set to Non Blocking */
309         flags |= O_NONBLOCK;
310         if (fcntl(info->fd, F_SETFL, flags) < 0) {
311                 RTE_LOG(WARNING, CHANNEL_MANAGER,
312                                 "Error(%s) setting non-blocking "
313                                 "socket for '%s'\n",
314                                 strerror(errno), info->channel_path);
315                 return -1;
316         }
317         return 0;
318 }
319
320 static int
321 setup_channel_info(struct virtual_machine_info **vm_info_dptr,
322                 struct channel_info **chan_info_dptr, unsigned channel_num)
323 {
324         struct channel_info *chan_info = *chan_info_dptr;
325         struct virtual_machine_info *vm_info = *vm_info_dptr;
326
327         chan_info->channel_num = channel_num;
328         chan_info->priv_info = (void *)vm_info;
329         chan_info->status = CHANNEL_MGR_CHANNEL_DISCONNECTED;
330         chan_info->type = CHANNEL_TYPE_BINARY;
331         if (open_non_blocking_channel(chan_info) < 0) {
332                 RTE_LOG(ERR, CHANNEL_MANAGER, "Could not open channel: "
333                                 "'%s' for VM '%s'\n",
334                                 chan_info->channel_path, vm_info->name);
335                 return -1;
336         }
337         if (add_channel_to_monitor(&chan_info) < 0) {
338                 RTE_LOG(ERR, CHANNEL_MANAGER, "Could add channel: "
339                                 "'%s' to epoll ctl for VM '%s'\n",
340                                 chan_info->channel_path, vm_info->name);
341                 return -1;
342
343         }
344         rte_spinlock_lock(&(vm_info->config_spinlock));
345         vm_info->num_channels++;
346         vm_info->channel_mask |= 1ULL << channel_num;
347         vm_info->channels[channel_num] = chan_info;
348         chan_info->status = CHANNEL_MGR_CHANNEL_CONNECTED;
349         rte_spinlock_unlock(&(vm_info->config_spinlock));
350         return 0;
351 }
352
353 static void
354 fifo_path(char *dst, unsigned int len)
355 {
356         snprintf(dst, len, "%sfifo", CHANNEL_MGR_SOCKET_PATH);
357 }
358
359 static int
360 setup_host_channel_info(struct channel_info **chan_info_dptr,
361                 unsigned int channel_num)
362 {
363         struct channel_info *chan_info = *chan_info_dptr;
364
365         chan_info->channel_num = channel_num;
366         chan_info->priv_info = (void *)NULL;
367         chan_info->status = CHANNEL_MGR_CHANNEL_DISCONNECTED;
368         chan_info->type = CHANNEL_TYPE_JSON;
369
370         fifo_path(chan_info->channel_path, sizeof(chan_info->channel_path));
371
372         if (open_host_channel(chan_info) < 0) {
373                 RTE_LOG(ERR, CHANNEL_MANAGER, "Could not open host channel: "
374                                 "'%s'\n",
375                                 chan_info->channel_path);
376                 return -1;
377         }
378         if (add_channel_to_monitor(&chan_info) < 0) {
379                 RTE_LOG(ERR, CHANNEL_MANAGER, "Could add channel: "
380                                 "'%s' to epoll ctl\n",
381                                 chan_info->channel_path);
382                 return -1;
383
384         }
385         chan_info->status = CHANNEL_MGR_CHANNEL_CONNECTED;
386         return 0;
387 }
388
389 int
390 add_all_channels(const char *vm_name)
391 {
392         DIR *d;
393         struct dirent *dir;
394         struct virtual_machine_info *vm_info;
395         struct channel_info *chan_info;
396         char *token, *remaining, *tail_ptr;
397         char socket_name[PATH_MAX];
398         unsigned channel_num;
399         int num_channels_enabled = 0;
400
401         /* verify VM exists */
402         vm_info = find_domain_by_name(vm_name);
403         if (vm_info == NULL) {
404                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' not found"
405                                 " during channel discovery\n", vm_name);
406                 return 0;
407         }
408         if (!virDomainIsActive(vm_info->domainPtr)) {
409                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' is not active\n", vm_name);
410                 vm_info->status = CHANNEL_MGR_VM_INACTIVE;
411                 return 0;
412         }
413         d = opendir(CHANNEL_MGR_SOCKET_PATH);
414         if (d == NULL) {
415                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error opening directory '%s': %s\n",
416                                 CHANNEL_MGR_SOCKET_PATH, strerror(errno));
417                 return -1;
418         }
419         while ((dir = readdir(d)) != NULL) {
420                 if (!strncmp(dir->d_name, ".", 1) ||
421                                 !strncmp(dir->d_name, "..", 2))
422                         continue;
423
424                 snprintf(socket_name, sizeof(socket_name), "%s", dir->d_name);
425                 remaining = socket_name;
426                 /* Extract vm_name from "<vm_name>.<channel_num>" */
427                 token = strsep(&remaining, ".");
428                 if (remaining == NULL)
429                         continue;
430                 if (strncmp(vm_name, token, CHANNEL_MGR_MAX_NAME_LEN))
431                         continue;
432
433                 /* remaining should contain only <channel_num> */
434                 errno = 0;
435                 channel_num = (unsigned)strtol(remaining, &tail_ptr, 0);
436                 if ((errno != 0) || (remaining[0] == '\0') ||
437                                 tail_ptr == NULL || (*tail_ptr != '\0')) {
438                         RTE_LOG(WARNING, CHANNEL_MANAGER, "Malformed channel name"
439                                         "'%s' found it should be in the form of "
440                                         "'<guest_name>.<channel_num>(decimal)'\n",
441                                         dir->d_name);
442                         continue;
443                 }
444                 if (channel_num >= CHANNEL_CMDS_MAX_VM_CHANNELS) {
445                         RTE_LOG(WARNING, CHANNEL_MANAGER, "Channel number(%u) is "
446                                         "greater than max allowable: %d, skipping '%s%s'\n",
447                                         channel_num, CHANNEL_CMDS_MAX_VM_CHANNELS-1,
448                                         CHANNEL_MGR_SOCKET_PATH, dir->d_name);
449                         continue;
450                 }
451                 /* if channel has not been added previously */
452                 if (channel_exists(vm_info, channel_num))
453                         continue;
454
455                 chan_info = rte_malloc(NULL, sizeof(*chan_info),
456                                 RTE_CACHE_LINE_SIZE);
457                 if (chan_info == NULL) {
458                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
459                                 "channel '%s%s'\n", CHANNEL_MGR_SOCKET_PATH, dir->d_name);
460                         continue;
461                 }
462
463                 snprintf(chan_info->channel_path,
464                                 sizeof(chan_info->channel_path), "%s%s",
465                                 CHANNEL_MGR_SOCKET_PATH, dir->d_name);
466
467                 if (setup_channel_info(&vm_info, &chan_info, channel_num) < 0) {
468                         rte_free(chan_info);
469                         continue;
470                 }
471
472                 num_channels_enabled++;
473         }
474         closedir(d);
475         return num_channels_enabled;
476 }
477
478 int
479 add_channels(const char *vm_name, unsigned *channel_list,
480                 unsigned len_channel_list)
481 {
482         struct virtual_machine_info *vm_info;
483         struct channel_info *chan_info;
484         char socket_path[PATH_MAX];
485         unsigned i;
486         int num_channels_enabled = 0;
487
488         vm_info = find_domain_by_name(vm_name);
489         if (vm_info == NULL) {
490                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add channels: VM '%s' "
491                                 "not found\n", vm_name);
492                 return 0;
493         }
494
495         if (!virDomainIsActive(vm_info->domainPtr)) {
496                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' is not active\n", vm_name);
497                 vm_info->status = CHANNEL_MGR_VM_INACTIVE;
498                 return 0;
499         }
500
501         for (i = 0; i < len_channel_list; i++) {
502
503                 if (channel_list[i] >= CHANNEL_CMDS_MAX_VM_CHANNELS) {
504                         RTE_LOG(INFO, CHANNEL_MANAGER, "Channel(%u) is out of range "
505                                                         "0...%d\n", channel_list[i],
506                                                         CHANNEL_CMDS_MAX_VM_CHANNELS-1);
507                         continue;
508                 }
509                 if (channel_exists(vm_info, channel_list[i])) {
510                         RTE_LOG(INFO, CHANNEL_MANAGER, "Channel already exists, skipping  "
511                                         "'%s.%u'\n", vm_name, i);
512                         continue;
513                 }
514
515                 snprintf(socket_path, sizeof(socket_path), "%s%s.%u",
516                                 CHANNEL_MGR_SOCKET_PATH, vm_name, channel_list[i]);
517                 errno = 0;
518                 if (access(socket_path, F_OK) < 0) {
519                         RTE_LOG(ERR, CHANNEL_MANAGER, "Channel path '%s' error: "
520                                         "%s\n", socket_path, strerror(errno));
521                         continue;
522                 }
523                 chan_info = rte_malloc(NULL, sizeof(*chan_info),
524                                 RTE_CACHE_LINE_SIZE);
525                 if (chan_info == NULL) {
526                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
527                                         "channel '%s'\n", socket_path);
528                         continue;
529                 }
530                 snprintf(chan_info->channel_path,
531                                 sizeof(chan_info->channel_path), "%s%s.%u",
532                                 CHANNEL_MGR_SOCKET_PATH, vm_name, channel_list[i]);
533                 if (setup_channel_info(&vm_info, &chan_info, channel_list[i]) < 0) {
534                         rte_free(chan_info);
535                         continue;
536                 }
537                 num_channels_enabled++;
538
539         }
540         return num_channels_enabled;
541 }
542
543 int
544 add_host_channel(void)
545 {
546         struct channel_info *chan_info;
547         char socket_path[PATH_MAX];
548         int num_channels_enabled = 0;
549         int ret;
550
551         fifo_path(socket_path, sizeof(socket_path));
552
553         ret = mkfifo(socket_path, 0660);
554         if ((errno != EEXIST) && (ret < 0)) {
555                 RTE_LOG(ERR, CHANNEL_MANAGER, "Cannot create fifo '%s' error: "
556                                 "%s\n", socket_path, strerror(errno));
557                 return 0;
558         }
559
560         if (access(socket_path, F_OK) < 0) {
561                 RTE_LOG(ERR, CHANNEL_MANAGER, "Channel path '%s' error: "
562                                 "%s\n", socket_path, strerror(errno));
563                 return 0;
564         }
565         chan_info = rte_malloc(NULL, sizeof(*chan_info), 0);
566         if (chan_info == NULL) {
567                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
568                                 "channel '%s'\n", socket_path);
569                 return 0;
570         }
571         snprintf(chan_info->channel_path,
572                         sizeof(chan_info->channel_path), "%s", socket_path);
573         if (setup_host_channel_info(&chan_info, 0) < 0) {
574                 rte_free(chan_info);
575                 return 0;
576         }
577         num_channels_enabled++;
578
579         return num_channels_enabled;
580 }
581
582 int
583 remove_channel(struct channel_info **chan_info_dptr)
584 {
585         struct virtual_machine_info *vm_info;
586         struct channel_info *chan_info = *chan_info_dptr;
587
588         close(chan_info->fd);
589
590         vm_info = (struct virtual_machine_info *)chan_info->priv_info;
591
592         rte_spinlock_lock(&(vm_info->config_spinlock));
593         vm_info->channel_mask &= ~(1ULL << chan_info->channel_num);
594         vm_info->num_channels--;
595         rte_spinlock_unlock(&(vm_info->config_spinlock));
596
597         rte_free(chan_info);
598         return 0;
599 }
600
601 int
602 set_channel_status_all(const char *vm_name, enum channel_status status)
603 {
604         struct virtual_machine_info *vm_info;
605         unsigned i;
606         uint64_t mask;
607         int num_channels_changed = 0;
608
609         if (!(status == CHANNEL_MGR_CHANNEL_CONNECTED ||
610                         status == CHANNEL_MGR_CHANNEL_DISABLED)) {
611                 RTE_LOG(ERR, CHANNEL_MANAGER, "Channels can only be enabled or "
612                                 "disabled: Unable to change status for VM '%s'\n", vm_name);
613         }
614         vm_info = find_domain_by_name(vm_name);
615         if (vm_info == NULL) {
616                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to disable channels: VM '%s' "
617                                 "not found\n", vm_name);
618                 return 0;
619         }
620
621         rte_spinlock_lock(&(vm_info->config_spinlock));
622         mask = vm_info->channel_mask;
623         ITERATIVE_BITMASK_CHECK_64(mask, i) {
624                 vm_info->channels[i]->status = status;
625                 num_channels_changed++;
626         }
627         rte_spinlock_unlock(&(vm_info->config_spinlock));
628         return num_channels_changed;
629
630 }
631
632 int
633 set_channel_status(const char *vm_name, unsigned *channel_list,
634                 unsigned len_channel_list, enum channel_status status)
635 {
636         struct virtual_machine_info *vm_info;
637         unsigned i;
638         int num_channels_changed = 0;
639
640         if (!(status == CHANNEL_MGR_CHANNEL_CONNECTED ||
641                         status == CHANNEL_MGR_CHANNEL_DISABLED)) {
642                 RTE_LOG(ERR, CHANNEL_MANAGER, "Channels can only be enabled or "
643                                 "disabled: Unable to change status for VM '%s'\n", vm_name);
644         }
645         vm_info = find_domain_by_name(vm_name);
646         if (vm_info == NULL) {
647                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add channels: VM '%s' "
648                                 "not found\n", vm_name);
649                 return 0;
650         }
651         for (i = 0; i < len_channel_list; i++) {
652                 if (channel_exists(vm_info, channel_list[i])) {
653                         rte_spinlock_lock(&(vm_info->config_spinlock));
654                         vm_info->channels[channel_list[i]]->status = status;
655                         rte_spinlock_unlock(&(vm_info->config_spinlock));
656                         num_channels_changed++;
657                 }
658         }
659         return num_channels_changed;
660 }
661
662 void
663 get_all_vm(int *num_vm, int *num_vcpu)
664 {
665
666         virNodeInfo node_info;
667         virDomainPtr *domptr;
668         uint64_t mask;
669         int i, ii, numVcpus[MAX_VCPUS], cpu, n_vcpus;
670         unsigned int jj;
671         const char *vm_name;
672         unsigned int domain_flags = VIR_CONNECT_LIST_DOMAINS_RUNNING |
673                                 VIR_CONNECT_LIST_DOMAINS_PERSISTENT;
674         unsigned int domain_flag = VIR_DOMAIN_VCPU_CONFIG;
675
676         if (!global_hypervisor_available)
677                 return;
678
679         memset(global_cpumaps, 0, CHANNEL_CMDS_MAX_CPUS*global_maplen);
680         if (virNodeGetInfo(global_vir_conn_ptr, &node_info)) {
681                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to retrieve node Info\n");
682                 return;
683         }
684
685         /* Returns number of pcpus */
686         global_n_host_cpus = (unsigned int)node_info.cpus;
687
688         /* Returns number of active domains */
689         *num_vm = virConnectListAllDomains(global_vir_conn_ptr, &domptr,
690                                         domain_flags);
691         if (*num_vm <= 0) {
692                 RTE_LOG(ERR, CHANNEL_MANAGER, "No Active Domains Running\n");
693                 return;
694         }
695
696         for (i = 0; i < *num_vm; i++) {
697
698                 /* Get Domain Names */
699                 vm_name = virDomainGetName(domptr[i]);
700                 lvm_info[i].vm_name = vm_name;
701
702                 /* Get Number of Vcpus */
703                 numVcpus[i] = virDomainGetVcpusFlags(domptr[i], domain_flag);
704
705                 /* Get Number of VCpus & VcpuPinInfo */
706                 n_vcpus = virDomainGetVcpuPinInfo(domptr[i],
707                                 numVcpus[i], global_cpumaps,
708                                 global_maplen, domain_flag);
709
710                 if ((int)n_vcpus > 0) {
711                         *num_vcpu = n_vcpus;
712                         lvm_info[i].num_cpus = n_vcpus;
713                 }
714
715                 /* Save pcpu in use by libvirt VMs */
716                 for (ii = 0; ii < n_vcpus; ii++) {
717                         mask = 0;
718                         for (jj = 0; jj < global_n_host_cpus; jj++) {
719                                 if (VIR_CPU_USABLE(global_cpumaps,
720                                                 global_maplen, ii, jj) > 0) {
721                                         mask |= 1ULL << jj;
722                                 }
723                         }
724                         ITERATIVE_BITMASK_CHECK_64(mask, cpu) {
725                                 lvm_info[i].pcpus[ii] = cpu;
726                         }
727                 }
728         }
729 }
730
731 int
732 get_info_vm(const char *vm_name, struct vm_info *info)
733 {
734         struct virtual_machine_info *vm_info;
735         unsigned i, channel_num = 0;
736         uint64_t mask;
737
738         vm_info = find_domain_by_name(vm_name);
739         if (vm_info == NULL) {
740                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM '%s' not found\n", vm_name);
741                 return -1;
742         }
743         info->status = CHANNEL_MGR_VM_ACTIVE;
744         if (!virDomainIsActive(vm_info->domainPtr))
745                 info->status = CHANNEL_MGR_VM_INACTIVE;
746
747         rte_spinlock_lock(&(vm_info->config_spinlock));
748
749         mask = vm_info->channel_mask;
750         ITERATIVE_BITMASK_CHECK_64(mask, i) {
751                 info->channels[channel_num].channel_num = i;
752                 memcpy(info->channels[channel_num].channel_path,
753                                 vm_info->channels[i]->channel_path, UNIX_PATH_MAX);
754                 info->channels[channel_num].status = vm_info->channels[i]->status;
755                 info->channels[channel_num].fd = vm_info->channels[i]->fd;
756                 channel_num++;
757         }
758
759         info->num_channels = channel_num;
760         info->num_vcpus = vm_info->info.nrVirtCpu;
761         rte_spinlock_unlock(&(vm_info->config_spinlock));
762
763         memcpy(info->name, vm_info->name, sizeof(vm_info->name));
764         for (i = 0; i < info->num_vcpus; i++) {
765                 info->pcpu_mask[i] = rte_atomic64_read(&vm_info->pcpu_mask[i]);
766         }
767         return 0;
768 }
769
770 int
771 add_vm(const char *vm_name)
772 {
773         struct virtual_machine_info *new_domain;
774         virDomainPtr dom_ptr;
775         int i;
776
777         if (find_domain_by_name(vm_name) != NULL) {
778                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add VM: VM '%s' "
779                                 "already exists\n", vm_name);
780                 return -1;
781         }
782
783         if (global_vir_conn_ptr == NULL) {
784                 RTE_LOG(ERR, CHANNEL_MANAGER, "No connection to hypervisor exists\n");
785                 return -1;
786         }
787         dom_ptr = virDomainLookupByName(global_vir_conn_ptr, vm_name);
788         if (dom_ptr == NULL) {
789                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error on VM lookup with libvirt: "
790                                 "VM '%s' not found\n", vm_name);
791                 return -1;
792         }
793
794         new_domain = rte_malloc("virtual_machine_info", sizeof(*new_domain),
795                         RTE_CACHE_LINE_SIZE);
796         if (new_domain == NULL) {
797                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to allocate memory for VM "
798                                 "info\n");
799                 return -1;
800         }
801         new_domain->domainPtr = dom_ptr;
802         if (virDomainGetInfo(new_domain->domainPtr, &new_domain->info) != 0) {
803                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to get libvirt VM info\n");
804                 rte_free(new_domain);
805                 return -1;
806         }
807         if (new_domain->info.nrVirtCpu > CHANNEL_CMDS_MAX_CPUS) {
808                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error the number of virtual CPUs(%u) is "
809                                 "greater than allowable(%d)\n", new_domain->info.nrVirtCpu,
810                                 CHANNEL_CMDS_MAX_CPUS);
811                 rte_free(new_domain);
812                 return -1;
813         }
814
815         for (i = 0; i < CHANNEL_CMDS_MAX_CPUS; i++) {
816                 rte_atomic64_init(&new_domain->pcpu_mask[i]);
817         }
818         if (update_pcpus_mask(new_domain) < 0) {
819                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting physical CPU pinning\n");
820                 rte_free(new_domain);
821                 return -1;
822         }
823         strncpy(new_domain->name, vm_name, sizeof(new_domain->name));
824         new_domain->name[sizeof(new_domain->name) - 1] = '\0';
825         new_domain->channel_mask = 0;
826         new_domain->num_channels = 0;
827
828         if (!virDomainIsActive(dom_ptr))
829                 new_domain->status = CHANNEL_MGR_VM_INACTIVE;
830         else
831                 new_domain->status = CHANNEL_MGR_VM_ACTIVE;
832
833         rte_spinlock_init(&(new_domain->config_spinlock));
834         LIST_INSERT_HEAD(&vm_list_head, new_domain, vms_info);
835         return 0;
836 }
837
838 int
839 remove_vm(const char *vm_name)
840 {
841         struct virtual_machine_info *vm_info = find_domain_by_name(vm_name);
842
843         if (vm_info == NULL) {
844                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to remove VM: VM '%s' "
845                                 "not found\n", vm_name);
846                 return -1;
847         }
848         rte_spinlock_lock(&vm_info->config_spinlock);
849         if (vm_info->num_channels != 0) {
850                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to remove VM '%s', there are "
851                                 "%"PRId8" channels still active\n",
852                                 vm_name, vm_info->num_channels);
853                 rte_spinlock_unlock(&vm_info->config_spinlock);
854                 return -1;
855         }
856         LIST_REMOVE(vm_info, vms_info);
857         rte_spinlock_unlock(&vm_info->config_spinlock);
858         rte_free(vm_info);
859         return 0;
860 }
861
862 static void
863 disconnect_hypervisor(void)
864 {
865         if (global_vir_conn_ptr != NULL) {
866                 virConnectClose(global_vir_conn_ptr);
867                 global_vir_conn_ptr = NULL;
868         }
869 }
870
871 static int
872 connect_hypervisor(const char *path)
873 {
874         if (global_vir_conn_ptr != NULL) {
875                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error connecting to %s, connection "
876                                 "already established\n", path);
877                 return -1;
878         }
879         global_vir_conn_ptr = virConnectOpen(path);
880         if (global_vir_conn_ptr == NULL) {
881                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error failed to open connection to "
882                                 "Hypervisor '%s'\n", path);
883                 return -1;
884         }
885         return 0;
886 }
887 int
888 channel_manager_init(const char *path __rte_unused)
889 {
890         virNodeInfo info;
891
892         LIST_INIT(&vm_list_head);
893         if (connect_hypervisor(path) < 0) {
894                 global_n_host_cpus = 64;
895                 global_hypervisor_available = 0;
896                 RTE_LOG(INFO, CHANNEL_MANAGER, "Unable to initialize channel manager\n");
897         } else {
898                 global_hypervisor_available = 1;
899
900                 global_maplen = VIR_CPU_MAPLEN(CHANNEL_CMDS_MAX_CPUS);
901
902                 global_vircpuinfo = rte_zmalloc(NULL,
903                                 sizeof(*global_vircpuinfo) *
904                                 CHANNEL_CMDS_MAX_CPUS, RTE_CACHE_LINE_SIZE);
905                 if (global_vircpuinfo == NULL) {
906                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for CPU Info\n");
907                         goto error;
908                 }
909                 global_cpumaps = rte_zmalloc(NULL,
910                                 CHANNEL_CMDS_MAX_CPUS * global_maplen,
911                                 RTE_CACHE_LINE_SIZE);
912                 if (global_cpumaps == NULL)
913                         goto error;
914
915                 if (virNodeGetInfo(global_vir_conn_ptr, &info)) {
916                         RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to retrieve node Info\n");
917                         goto error;
918                 }
919                 global_n_host_cpus = (unsigned int)info.cpus;
920         }
921
922
923
924         if (global_n_host_cpus > CHANNEL_CMDS_MAX_CPUS) {
925                 RTE_LOG(WARNING, CHANNEL_MANAGER, "The number of host CPUs(%u) exceeds the "
926                                 "maximum of %u. No cores over %u should be used.\n",
927                                 global_n_host_cpus, CHANNEL_CMDS_MAX_CPUS,
928                                 CHANNEL_CMDS_MAX_CPUS - 1);
929                 global_n_host_cpus = CHANNEL_CMDS_MAX_CPUS;
930         }
931
932         return 0;
933 error:
934         if (global_hypervisor_available)
935                 disconnect_hypervisor();
936         return -1;
937 }
938
939 void
940 channel_manager_exit(void)
941 {
942         unsigned i;
943         uint64_t mask;
944         struct virtual_machine_info *vm_info;
945
946         LIST_FOREACH(vm_info, &vm_list_head, vms_info) {
947
948                 rte_spinlock_lock(&(vm_info->config_spinlock));
949
950                 mask = vm_info->channel_mask;
951                 ITERATIVE_BITMASK_CHECK_64(mask, i) {
952                         remove_channel_from_monitor(vm_info->channels[i]);
953                         close(vm_info->channels[i]->fd);
954                         rte_free(vm_info->channels[i]);
955                 }
956                 rte_spinlock_unlock(&(vm_info->config_spinlock));
957
958                 LIST_REMOVE(vm_info, vms_info);
959                 rte_free(vm_info);
960         }
961
962         if (global_hypervisor_available) {
963                 /* Only needed if hypervisor available */
964                 rte_free(global_cpumaps);
965                 rte_free(global_vircpuinfo);
966                 disconnect_hypervisor();
967         }
968 }