xref: /dpdk/examples/vm_power_manager/channel_manager.c (revision fea1d908d39989a27890b29b5c0ec94c85c8257b)
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 	rte_free(global_cpumaps);
804 	rte_free(global_vircpuinfo);
805 	disconnect_hypervisor();
806 }
807