vhost: combine select with sleep
[dpdk.git] / examples / vm_power_manager / channel_manager.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/un.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <inttypes.h>
40 #include <dirent.h>
41 #include <errno.h>
42
43 #include <sys/queue.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/select.h>
47
48 #include <rte_config.h>
49 #include <rte_malloc.h>
50 #include <rte_memory.h>
51 #include <rte_mempool.h>
52 #include <rte_log.h>
53 #include <rte_atomic.h>
54 #include <rte_spinlock.h>
55
56 #include <libvirt/libvirt.h>
57
58 #include "channel_manager.h"
59 #include "channel_commands.h"
60 #include "channel_monitor.h"
61
62
63 #define RTE_LOGTYPE_CHANNEL_MANAGER RTE_LOGTYPE_USER1
64
65 #define ITERATIVE_BITMASK_CHECK_64(mask_u64b, i) \
66                 for (i = 0; mask_u64b; mask_u64b &= ~(1ULL << i++)) \
67                 if ((mask_u64b >> i) & 1) \
68
69 /* Global pointer to libvirt connection */
70 static virConnectPtr global_vir_conn_ptr;
71
72 static unsigned char *global_cpumaps;
73 static virVcpuInfo *global_vircpuinfo;
74 static size_t global_maplen;
75
76 static unsigned global_n_host_cpus;
77
78 /*
79  * Represents a single Virtual Machine
80  */
81 struct virtual_machine_info {
82         char name[CHANNEL_MGR_MAX_NAME_LEN];
83         rte_atomic64_t pcpu_mask[CHANNEL_CMDS_MAX_CPUS];
84         struct channel_info *channels[CHANNEL_CMDS_MAX_VM_CHANNELS];
85         uint64_t channel_mask;
86         uint8_t num_channels;
87         enum vm_status status;
88         virDomainPtr domainPtr;
89         virDomainInfo info;
90         rte_spinlock_t config_spinlock;
91         LIST_ENTRY(virtual_machine_info) vms_info;
92 };
93
94 LIST_HEAD(, virtual_machine_info) vm_list_head;
95
96 static struct virtual_machine_info *
97 find_domain_by_name(const char *name)
98 {
99         struct virtual_machine_info *info;
100         LIST_FOREACH(info, &vm_list_head, vms_info) {
101                 if (!strncmp(info->name, name, CHANNEL_MGR_MAX_NAME_LEN-1))
102                         return info;
103         }
104         return NULL;
105 }
106
107 static int
108 update_pcpus_mask(struct virtual_machine_info *vm_info)
109 {
110         virVcpuInfoPtr cpuinfo;
111         unsigned i, j;
112         int n_vcpus;
113         uint64_t mask;
114
115         memset(global_cpumaps, 0, CHANNEL_CMDS_MAX_CPUS*global_maplen);
116
117         if (!virDomainIsActive(vm_info->domainPtr)) {
118                 n_vcpus = virDomainGetVcpuPinInfo(vm_info->domainPtr,
119                                 vm_info->info.nrVirtCpu, global_cpumaps, global_maplen,
120                                 VIR_DOMAIN_AFFECT_CONFIG);
121                 if (n_vcpus < 0) {
122                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting vCPU info for "
123                                         "in-active VM '%s'\n", vm_info->name);
124                         return -1;
125                 }
126                 goto update_pcpus;
127         }
128
129         memset(global_vircpuinfo, 0, sizeof(*global_vircpuinfo)*
130                         CHANNEL_CMDS_MAX_CPUS);
131
132         cpuinfo = global_vircpuinfo;
133
134         n_vcpus = virDomainGetVcpus(vm_info->domainPtr, cpuinfo,
135                         CHANNEL_CMDS_MAX_CPUS, global_cpumaps, global_maplen);
136         if (n_vcpus < 0) {
137                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting vCPU info for "
138                                 "active VM '%s'\n", vm_info->name);
139                 return -1;
140         }
141 update_pcpus:
142         if (n_vcpus >= CHANNEL_CMDS_MAX_CPUS) {
143                 RTE_LOG(ERR, CHANNEL_MANAGER, "Number of vCPUS(%u) is out of range "
144                                 "0...%d\n", n_vcpus, CHANNEL_CMDS_MAX_CPUS-1);
145                 return -1;
146         }
147         if (n_vcpus != vm_info->info.nrVirtCpu) {
148                 RTE_LOG(INFO, CHANNEL_MANAGER, "Updating the number of vCPUs for VM '%s"
149                                 " from %d -> %d\n", vm_info->name, vm_info->info.nrVirtCpu,
150                                 n_vcpus);
151                 vm_info->info.nrVirtCpu = n_vcpus;
152         }
153         for (i = 0; i < vm_info->info.nrVirtCpu; i++) {
154                 mask = 0;
155                 for (j = 0; j < global_n_host_cpus; j++) {
156                         if (VIR_CPU_USABLE(global_cpumaps, global_maplen, i, j) > 0) {
157                                 mask |= 1ULL << j;
158                         }
159                 }
160                 rte_atomic64_set(&vm_info->pcpu_mask[i], mask);
161         }
162         return 0;
163 }
164
165 int
166 set_pcpus_mask(char *vm_name, unsigned vcpu, uint64_t core_mask)
167 {
168         unsigned i = 0;
169         int flags = VIR_DOMAIN_AFFECT_LIVE|VIR_DOMAIN_AFFECT_CONFIG;
170         struct virtual_machine_info *vm_info;
171         uint64_t mask = core_mask;
172
173         if (vcpu >= CHANNEL_CMDS_MAX_CPUS) {
174                 RTE_LOG(ERR, CHANNEL_MANAGER, "vCPU(%u) exceeds max allowable(%d)\n",
175                                 vcpu, CHANNEL_CMDS_MAX_CPUS-1);
176                 return -1;
177         }
178
179         vm_info = find_domain_by_name(vm_name);
180         if (vm_info == NULL) {
181                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM '%s' not found\n", vm_name);
182                 return -1;
183         }
184
185         if (!virDomainIsActive(vm_info->domainPtr)) {
186                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to set vCPU(%u) to pCPU "
187                                 "mask(0x%"PRIx64") for VM '%s', VM is not active\n",
188                                 vcpu, core_mask, vm_info->name);
189                 return -1;
190         }
191
192         if (vcpu >= vm_info->info.nrVirtCpu) {
193                 RTE_LOG(ERR, CHANNEL_MANAGER, "vCPU(%u) exceeds the assigned number of "
194                                 "vCPUs(%u)\n", vcpu, vm_info->info.nrVirtCpu);
195                 return -1;
196         }
197         memset(global_cpumaps, 0 , CHANNEL_CMDS_MAX_CPUS * global_maplen);
198         ITERATIVE_BITMASK_CHECK_64(mask, i) {
199                 VIR_USE_CPU(global_cpumaps, i);
200                 if (i >= global_n_host_cpus) {
201                         RTE_LOG(ERR, CHANNEL_MANAGER, "CPU(%u) exceeds the available "
202                                         "number of CPUs(%u)\n", i, global_n_host_cpus);
203                         return -1;
204                 }
205         }
206         if (virDomainPinVcpuFlags(vm_info->domainPtr, vcpu, global_cpumaps,
207                         global_maplen, flags) < 0) {
208                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to set vCPU(%u) to pCPU "
209                                 "mask(0x%"PRIx64") for VM '%s'\n", vcpu, core_mask,
210                                 vm_info->name);
211                 return -1;
212         }
213         rte_atomic64_set(&vm_info->pcpu_mask[vcpu], core_mask);
214         return 0;
215
216 }
217
218 int
219 set_pcpu(char *vm_name, unsigned vcpu, unsigned core_num)
220 {
221         uint64_t mask = 1ULL << core_num;
222
223         return set_pcpus_mask(vm_name, vcpu, mask);
224 }
225
226 uint64_t
227 get_pcpus_mask(struct channel_info *chan_info, unsigned vcpu)
228 {
229         struct virtual_machine_info *vm_info =
230                         (struct virtual_machine_info *)chan_info->priv_info;
231         return rte_atomic64_read(&vm_info->pcpu_mask[vcpu]);
232 }
233
234 static inline int
235 channel_exists(struct virtual_machine_info *vm_info, unsigned channel_num)
236 {
237         rte_spinlock_lock(&(vm_info->config_spinlock));
238         if (vm_info->channel_mask & (1ULL << channel_num)) {
239                 rte_spinlock_unlock(&(vm_info->config_spinlock));
240                 return 1;
241         }
242         rte_spinlock_unlock(&(vm_info->config_spinlock));
243         return 0;
244 }
245
246
247
248 static int
249 open_non_blocking_channel(struct channel_info *info)
250 {
251         int ret, flags;
252         struct sockaddr_un sock_addr;
253         fd_set soc_fd_set;
254         struct timeval tv;
255
256         info->fd = socket(AF_UNIX, SOCK_STREAM, 0);
257         if (info->fd == -1) {
258                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error(%s) creating socket for '%s'\n",
259                                 strerror(errno),
260                                 info->channel_path);
261                 return -1;
262         }
263         sock_addr.sun_family = AF_UNIX;
264         memcpy(&sock_addr.sun_path, info->channel_path,
265                         strlen(info->channel_path)+1);
266
267         /* Get current flags */
268         flags = fcntl(info->fd, F_GETFL, 0);
269         if (flags < 0) {
270                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) fcntl get flags socket for"
271                                 "'%s'\n", strerror(errno), info->channel_path);
272                 return 1;
273         }
274         /* Set to Non Blocking */
275         flags |= O_NONBLOCK;
276         if (fcntl(info->fd, F_SETFL, flags) < 0) {
277                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) setting non-blocking "
278                                 "socket for '%s'\n", strerror(errno), info->channel_path);
279                 return -1;
280         }
281         ret = connect(info->fd, (struct sockaddr *)&sock_addr,
282                         sizeof(sock_addr));
283         if (ret < 0) {
284                 /* ECONNREFUSED error is given when VM is not active */
285                 if (errno == ECONNREFUSED) {
286                         RTE_LOG(WARNING, CHANNEL_MANAGER, "VM is not active or has not "
287                                         "activated its endpoint to channel %s\n",
288                                         info->channel_path);
289                         return -1;
290                 }
291                 /* Wait for tv_sec if in progress */
292                 else if (errno == EINPROGRESS) {
293                         tv.tv_sec = 2;
294                         tv.tv_usec = 0;
295                         FD_ZERO(&soc_fd_set);
296                         FD_SET(info->fd, &soc_fd_set);
297                         if (select(info->fd+1, NULL, &soc_fd_set, NULL, &tv) > 0) {
298                                 RTE_LOG(WARNING, CHANNEL_MANAGER, "Timeout or error on channel "
299                                                 "'%s'\n", info->channel_path);
300                                 return -1;
301                         }
302                 } else {
303                         /* Any other error */
304                         RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) connecting socket"
305                                         " for '%s'\n", strerror(errno), info->channel_path);
306                         return -1;
307                 }
308         }
309         return 0;
310 }
311
312 static int
313 setup_channel_info(struct virtual_machine_info **vm_info_dptr,
314                 struct channel_info **chan_info_dptr, unsigned channel_num)
315 {
316         struct channel_info *chan_info = *chan_info_dptr;
317         struct virtual_machine_info *vm_info = *vm_info_dptr;
318
319         chan_info->channel_num = channel_num;
320         chan_info->priv_info = (void *)vm_info;
321         chan_info->status = CHANNEL_MGR_CHANNEL_DISCONNECTED;
322         if (open_non_blocking_channel(chan_info) < 0) {
323                 RTE_LOG(ERR, CHANNEL_MANAGER, "Could not open channel: "
324                                 "'%s' for VM '%s'\n",
325                                 chan_info->channel_path, vm_info->name);
326                 return -1;
327         }
328         if (add_channel_to_monitor(&chan_info) < 0) {
329                 RTE_LOG(ERR, CHANNEL_MANAGER, "Could add channel: "
330                                 "'%s' to epoll ctl for VM '%s'\n",
331                                 chan_info->channel_path, vm_info->name);
332                 return -1;
333
334         }
335         rte_spinlock_lock(&(vm_info->config_spinlock));
336         vm_info->num_channels++;
337         vm_info->channel_mask |= 1ULL << channel_num;
338         vm_info->channels[channel_num] = chan_info;
339         chan_info->status = CHANNEL_MGR_CHANNEL_CONNECTED;
340         rte_spinlock_unlock(&(vm_info->config_spinlock));
341         return 0;
342 }
343
344 int
345 add_all_channels(const char *vm_name)
346 {
347         DIR *d;
348         struct dirent *dir;
349         struct virtual_machine_info *vm_info;
350         struct channel_info *chan_info;
351         char *token, *remaining, *tail_ptr;
352         char socket_name[PATH_MAX];
353         unsigned channel_num;
354         int num_channels_enabled = 0;
355
356         /* verify VM exists */
357         vm_info = find_domain_by_name(vm_name);
358         if (vm_info == NULL) {
359                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' not found"
360                                 " during channel discovery\n", vm_name);
361                 return 0;
362         }
363         if (!virDomainIsActive(vm_info->domainPtr)) {
364                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' is not active\n", vm_name);
365                 vm_info->status = CHANNEL_MGR_VM_INACTIVE;
366                 return 0;
367         }
368         d = opendir(CHANNEL_MGR_SOCKET_PATH);
369         if (d == NULL) {
370                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error opening directory '%s': %s\n",
371                                 CHANNEL_MGR_SOCKET_PATH, strerror(errno));
372                 return -1;
373         }
374         while ((dir = readdir(d)) != NULL) {
375                 if (!strncmp(dir->d_name, ".", 1) ||
376                                 !strncmp(dir->d_name, "..", 2))
377                         continue;
378
379                 snprintf(socket_name, sizeof(socket_name), "%s", dir->d_name);
380                 remaining = socket_name;
381                 /* Extract vm_name from "<vm_name>.<channel_num>" */
382                 token = strsep(&remaining, ".");
383                 if (remaining == NULL)
384                         continue;
385                 if (strncmp(vm_name, token, CHANNEL_MGR_MAX_NAME_LEN))
386                         continue;
387
388                 /* remaining should contain only <channel_num> */
389                 errno = 0;
390                 channel_num = (unsigned)strtol(remaining, &tail_ptr, 0);
391                 if ((errno != 0) || (remaining[0] == '\0') ||
392                                 tail_ptr == NULL || (*tail_ptr != '\0')) {
393                         RTE_LOG(WARNING, CHANNEL_MANAGER, "Malformed channel name"
394                                         "'%s' found it should be in the form of "
395                                         "'<guest_name>.<channel_num>(decimal)'\n",
396                                         dir->d_name);
397                         continue;
398                 }
399                 if (channel_num >= CHANNEL_CMDS_MAX_VM_CHANNELS) {
400                         RTE_LOG(WARNING, CHANNEL_MANAGER, "Channel number(%u) is "
401                                         "greater than max allowable: %d, skipping '%s%s'\n",
402                                         channel_num, CHANNEL_CMDS_MAX_VM_CHANNELS-1,
403                                         CHANNEL_MGR_SOCKET_PATH, dir->d_name);
404                         continue;
405                 }
406                 /* if channel has not been added previously */
407                 if (channel_exists(vm_info, channel_num))
408                         continue;
409
410                 chan_info = rte_malloc(NULL, sizeof(*chan_info),
411                                 RTE_CACHE_LINE_SIZE);
412                 if (chan_info == NULL) {
413                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
414                                 "channel '%s%s'\n", CHANNEL_MGR_SOCKET_PATH, dir->d_name);
415                         continue;
416                 }
417
418                 snprintf(chan_info->channel_path,
419                                 sizeof(chan_info->channel_path), "%s%s",
420                                 CHANNEL_MGR_SOCKET_PATH, dir->d_name);
421
422                 if (setup_channel_info(&vm_info, &chan_info, channel_num) < 0) {
423                         rte_free(chan_info);
424                         continue;
425                 }
426
427                 num_channels_enabled++;
428         }
429         closedir(d);
430         return num_channels_enabled;
431 }
432
433 int
434 add_channels(const char *vm_name, unsigned *channel_list,
435                 unsigned len_channel_list)
436 {
437         struct virtual_machine_info *vm_info;
438         struct channel_info *chan_info;
439         char socket_path[PATH_MAX];
440         unsigned i;
441         int num_channels_enabled = 0;
442
443         vm_info = find_domain_by_name(vm_name);
444         if (vm_info == NULL) {
445                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add channels: VM '%s' "
446                                 "not found\n", vm_name);
447                 return 0;
448         }
449
450         if (!virDomainIsActive(vm_info->domainPtr)) {
451                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' is not active\n", vm_name);
452                 vm_info->status = CHANNEL_MGR_VM_INACTIVE;
453                 return 0;
454         }
455
456         for (i = 0; i < len_channel_list; i++) {
457
458                 if (channel_list[i] >= CHANNEL_CMDS_MAX_VM_CHANNELS) {
459                         RTE_LOG(INFO, CHANNEL_MANAGER, "Channel(%u) is out of range "
460                                                         "0...%d\n", channel_list[i],
461                                                         CHANNEL_CMDS_MAX_VM_CHANNELS-1);
462                         continue;
463                 }
464                 if (channel_exists(vm_info, channel_list[i])) {
465                         RTE_LOG(INFO, CHANNEL_MANAGER, "Channel already exists, skipping  "
466                                         "'%s.%u'\n", vm_name, i);
467                         continue;
468                 }
469
470                 snprintf(socket_path, sizeof(socket_path), "%s%s.%u",
471                                 CHANNEL_MGR_SOCKET_PATH, vm_name, channel_list[i]);
472                 errno = 0;
473                 if (access(socket_path, F_OK) < 0) {
474                         RTE_LOG(ERR, CHANNEL_MANAGER, "Channel path '%s' error: "
475                                         "%s\n", socket_path, strerror(errno));
476                         continue;
477                 }
478                 chan_info = rte_malloc(NULL, sizeof(*chan_info),
479                                 RTE_CACHE_LINE_SIZE);
480                 if (chan_info == NULL) {
481                         RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
482                                         "channel '%s'\n", socket_path);
483                         continue;
484                 }
485                 snprintf(chan_info->channel_path,
486                                 sizeof(chan_info->channel_path), "%s%s.%u",
487                                 CHANNEL_MGR_SOCKET_PATH, vm_name, channel_list[i]);
488                 if (setup_channel_info(&vm_info, &chan_info, channel_list[i]) < 0) {
489                         rte_free(chan_info);
490                         continue;
491                 }
492                 num_channels_enabled++;
493
494         }
495         return num_channels_enabled;
496 }
497
498 int
499 remove_channel(struct channel_info **chan_info_dptr)
500 {
501         struct virtual_machine_info *vm_info;
502         struct channel_info *chan_info = *chan_info_dptr;
503
504         close(chan_info->fd);
505
506         vm_info = (struct virtual_machine_info *)chan_info->priv_info;
507
508         rte_spinlock_lock(&(vm_info->config_spinlock));
509         vm_info->channel_mask &= ~(1ULL << chan_info->channel_num);
510         vm_info->num_channels--;
511         rte_spinlock_unlock(&(vm_info->config_spinlock));
512
513         rte_free(chan_info);
514         return 0;
515 }
516
517 int
518 set_channel_status_all(const char *vm_name, enum channel_status status)
519 {
520         struct virtual_machine_info *vm_info;
521         unsigned i;
522         uint64_t mask;
523         int num_channels_changed = 0;
524
525         if (!(status == CHANNEL_MGR_CHANNEL_CONNECTED ||
526                         status == CHANNEL_MGR_CHANNEL_DISABLED)) {
527                 RTE_LOG(ERR, CHANNEL_MANAGER, "Channels can only be enabled or "
528                                 "disabled: Unable to change status for VM '%s'\n", vm_name);
529         }
530         vm_info = find_domain_by_name(vm_name);
531         if (vm_info == NULL) {
532                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to disable channels: VM '%s' "
533                                 "not found\n", vm_name);
534                 return 0;
535         }
536
537         rte_spinlock_lock(&(vm_info->config_spinlock));
538         mask = vm_info->channel_mask;
539         ITERATIVE_BITMASK_CHECK_64(mask, i) {
540                 vm_info->channels[i]->status = status;
541                 num_channels_changed++;
542         }
543         rte_spinlock_unlock(&(vm_info->config_spinlock));
544         return num_channels_changed;
545
546 }
547
548 int
549 set_channel_status(const char *vm_name, unsigned *channel_list,
550                 unsigned len_channel_list, enum channel_status status)
551 {
552         struct virtual_machine_info *vm_info;
553         unsigned i;
554         int num_channels_changed = 0;
555
556         if (!(status == CHANNEL_MGR_CHANNEL_CONNECTED ||
557                         status == CHANNEL_MGR_CHANNEL_DISABLED)) {
558                 RTE_LOG(ERR, CHANNEL_MANAGER, "Channels can only be enabled or "
559                                 "disabled: Unable to change status for VM '%s'\n", vm_name);
560         }
561         vm_info = find_domain_by_name(vm_name);
562         if (vm_info == NULL) {
563                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add channels: VM '%s' "
564                                 "not found\n", vm_name);
565                 return 0;
566         }
567         for (i = 0; i < len_channel_list; i++) {
568                 if (channel_exists(vm_info, channel_list[i])) {
569                         rte_spinlock_lock(&(vm_info->config_spinlock));
570                         vm_info->channels[channel_list[i]]->status = status;
571                         rte_spinlock_unlock(&(vm_info->config_spinlock));
572                         num_channels_changed++;
573                 }
574         }
575         return num_channels_changed;
576 }
577
578 int
579 get_info_vm(const char *vm_name, struct vm_info *info)
580 {
581         struct virtual_machine_info *vm_info;
582         unsigned i, channel_num = 0;
583         uint64_t mask;
584
585         vm_info = find_domain_by_name(vm_name);
586         if (vm_info == NULL) {
587                 RTE_LOG(ERR, CHANNEL_MANAGER, "VM '%s' not found\n", vm_name);
588                 return -1;
589         }
590         info->status = CHANNEL_MGR_VM_ACTIVE;
591         if (!virDomainIsActive(vm_info->domainPtr))
592                 info->status = CHANNEL_MGR_VM_INACTIVE;
593
594         rte_spinlock_lock(&(vm_info->config_spinlock));
595
596         mask = vm_info->channel_mask;
597         ITERATIVE_BITMASK_CHECK_64(mask, i) {
598                 info->channels[channel_num].channel_num = i;
599                 memcpy(info->channels[channel_num].channel_path,
600                                 vm_info->channels[i]->channel_path, UNIX_PATH_MAX);
601                 info->channels[channel_num].status = vm_info->channels[i]->status;
602                 info->channels[channel_num].fd = vm_info->channels[i]->fd;
603                 channel_num++;
604         }
605
606         info->num_channels = channel_num;
607         info->num_vcpus = vm_info->info.nrVirtCpu;
608         rte_spinlock_unlock(&(vm_info->config_spinlock));
609
610         memcpy(info->name, vm_info->name, sizeof(vm_info->name));
611         for (i = 0; i < info->num_vcpus; i++) {
612                 info->pcpu_mask[i] = rte_atomic64_read(&vm_info->pcpu_mask[i]);
613         }
614         return 0;
615 }
616
617 int
618 add_vm(const char *vm_name)
619 {
620         struct virtual_machine_info *new_domain;
621         virDomainPtr dom_ptr;
622         int i;
623
624         if (find_domain_by_name(vm_name) != NULL) {
625                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add VM: VM '%s' "
626                                 "already exists\n", vm_name);
627                 return -1;
628         }
629
630         if (global_vir_conn_ptr == NULL) {
631                 RTE_LOG(ERR, CHANNEL_MANAGER, "No connection to hypervisor exists\n");
632                 return -1;
633         }
634         dom_ptr = virDomainLookupByName(global_vir_conn_ptr, vm_name);
635         if (dom_ptr == NULL) {
636                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error on VM lookup with libvirt: "
637                                 "VM '%s' not found\n", vm_name);
638                 return -1;
639         }
640
641         new_domain = rte_malloc("virtual_machine_info", sizeof(*new_domain),
642                         RTE_CACHE_LINE_SIZE);
643         if (new_domain == NULL) {
644                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to allocate memory for VM "
645                                 "info\n");
646                 return -1;
647         }
648         new_domain->domainPtr = dom_ptr;
649         if (virDomainGetInfo(new_domain->domainPtr, &new_domain->info) != 0) {
650                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to get libvirt VM info\n");
651                 rte_free(new_domain);
652                 return -1;
653         }
654         if (new_domain->info.nrVirtCpu > CHANNEL_CMDS_MAX_CPUS) {
655                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error the number of virtual CPUs(%u) is "
656                                 "greater than allowable(%d)\n", new_domain->info.nrVirtCpu,
657                                 CHANNEL_CMDS_MAX_CPUS);
658                 rte_free(new_domain);
659                 return -1;
660         }
661
662         for (i = 0; i < CHANNEL_CMDS_MAX_CPUS; i++) {
663                 rte_atomic64_init(&new_domain->pcpu_mask[i]);
664         }
665         if (update_pcpus_mask(new_domain) < 0) {
666                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting physical CPU pinning\n");
667                 rte_free(new_domain);
668                 return -1;
669         }
670         strncpy(new_domain->name, vm_name, sizeof(new_domain->name));
671         new_domain->channel_mask = 0;
672         new_domain->num_channels = 0;
673
674         if (!virDomainIsActive(dom_ptr))
675                 new_domain->status = CHANNEL_MGR_VM_INACTIVE;
676         else
677                 new_domain->status = CHANNEL_MGR_VM_ACTIVE;
678
679         rte_spinlock_init(&(new_domain->config_spinlock));
680         LIST_INSERT_HEAD(&vm_list_head, new_domain, vms_info);
681         return 0;
682 }
683
684 int
685 remove_vm(const char *vm_name)
686 {
687         struct virtual_machine_info *vm_info = find_domain_by_name(vm_name);
688
689         if (vm_info == NULL) {
690                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to remove VM: VM '%s' "
691                                 "not found\n", vm_name);
692                 return -1;
693         }
694         rte_spinlock_lock(&vm_info->config_spinlock);
695         if (vm_info->num_channels != 0) {
696                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to remove VM '%s', there are "
697                                 "%"PRId8" channels still active\n",
698                                 vm_name, vm_info->num_channels);
699                 rte_spinlock_unlock(&vm_info->config_spinlock);
700                 return -1;
701         }
702         LIST_REMOVE(vm_info, vms_info);
703         rte_spinlock_unlock(&vm_info->config_spinlock);
704         rte_free(vm_info);
705         return 0;
706 }
707
708 static void
709 disconnect_hypervisor(void)
710 {
711         if (global_vir_conn_ptr != NULL) {
712                 virConnectClose(global_vir_conn_ptr);
713                 global_vir_conn_ptr = NULL;
714         }
715 }
716
717 static int
718 connect_hypervisor(const char *path)
719 {
720         if (global_vir_conn_ptr != NULL) {
721                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error connecting to %s, connection "
722                                 "already established\n", path);
723                 return -1;
724         }
725         global_vir_conn_ptr = virConnectOpen(path);
726         if (global_vir_conn_ptr == NULL) {
727                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error failed to open connection to "
728                                 "Hypervisor '%s'\n", path);
729                 return -1;
730         }
731         return 0;
732 }
733
734 int
735 channel_manager_init(const char *path)
736 {
737         int n_cpus;
738
739         LIST_INIT(&vm_list_head);
740         if (connect_hypervisor(path) < 0) {
741                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to initialize channel manager\n");
742                 return -1;
743         }
744
745         global_maplen = VIR_CPU_MAPLEN(CHANNEL_CMDS_MAX_CPUS);
746
747         global_vircpuinfo = rte_zmalloc(NULL, sizeof(*global_vircpuinfo) *
748                         CHANNEL_CMDS_MAX_CPUS, RTE_CACHE_LINE_SIZE);
749         if (global_vircpuinfo == NULL) {
750                 RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for CPU Info\n");
751                 goto error;
752         }
753         global_cpumaps = rte_zmalloc(NULL, CHANNEL_CMDS_MAX_CPUS * global_maplen,
754                         RTE_CACHE_LINE_SIZE);
755         if (global_cpumaps == NULL) {
756                 goto error;
757         }
758
759         n_cpus = virNodeGetCPUMap(global_vir_conn_ptr, NULL, NULL, 0);
760         if (n_cpus <= 0) {
761                 RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to get the number of Host "
762                                 "CPUs\n");
763                 goto error;
764         }
765         global_n_host_cpus = (unsigned)n_cpus;
766
767         if (global_n_host_cpus > CHANNEL_CMDS_MAX_CPUS) {
768                 RTE_LOG(ERR, CHANNEL_MANAGER, "The number of host CPUs(%u) exceeds the "
769                                 "maximum of %u\n", global_n_host_cpus, CHANNEL_CMDS_MAX_CPUS);
770                 goto error;
771
772         }
773
774         return 0;
775 error:
776         disconnect_hypervisor();
777         return -1;
778 }
779
780 void
781 channel_manager_exit(void)
782 {
783         unsigned i;
784         uint64_t mask;
785         struct virtual_machine_info *vm_info;
786
787         LIST_FOREACH(vm_info, &vm_list_head, vms_info) {
788
789                 rte_spinlock_lock(&(vm_info->config_spinlock));
790
791                 mask = vm_info->channel_mask;
792                 ITERATIVE_BITMASK_CHECK_64(mask, i) {
793                         remove_channel_from_monitor(vm_info->channels[i]);
794                         close(vm_info->channels[i]->fd);
795                         rte_free(vm_info->channels[i]);
796                 }
797                 rte_spinlock_unlock(&(vm_info->config_spinlock));
798
799                 LIST_REMOVE(vm_info, vms_info);
800                 rte_free(vm_info);
801         }
802
803         if (global_cpumaps != NULL)
804                 rte_free(global_cpumaps);
805         if (global_vircpuinfo != NULL)
806                 rte_free(global_vircpuinfo);
807         disconnect_hypervisor();
808 }