DPDK patches and discussions
 help / color / mirror / Atom feed
From: Neil Horman <nhorman@tuxdriver.com>
To: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Cc: dev@dpdk.org
Subject: Re: [dpdk-dev] [PATCH v6 01/10] Channel Manager and Monitor for VM Power Management(Host).
Date: Sat, 29 Nov 2014 10:21:51 -0500	[thread overview]
Message-ID: <20141129152151.GB14345@localhost.localdomain> (raw)
In-Reply-To: <1416932291-13162-2-git-send-email-pablo.de.lara.guarch@intel.com>

On Tue, Nov 25, 2014 at 04:18:02PM +0000, Pablo de Lara wrote:
> From: Alan Carew <alan.carew@intel.com>
> 
> The manager is responsible for adding communications channels to the Monitor
> thread, tracking and reporting VM state and employs the libvirt API for
> synchronization with the KVM Hypervisor. The manager interacts with the
> Hypervisor to discover the mapping of virtual CPUS(vCPUs) to the host
> physical CPUS(pCPUs) and to inspect the VM running state.
> 
> The manager provides the following functionality to the CLI:
> 1) Connect to a libvirtd instance, default: qemu:///system
> 2) Add a VM to an internal list, each VM is identified by a "name" which must
>    correspond a valid libvirt Domain Name.
> 3) Add communication channels associated with a VM to the epoll based Monitor
>    thread.
>    The channels must exist and be in the form of:
>    /tmp/powermonitor/<vm_name>.<channel_number>. Each channel is a
>    Virtio-Serial endpoint configured as an AF_UNIX file socket and opened in
>    non-blocking mode.
>    Each VM can have a maximum of 64 channels associated with it.
> 4) Disable or re-enable VM communication channels, channels once added to the
>    Monitor thread remain in that threads control, however acting on channel
>    requests can be disabled and renabled via CLI.
> 
> The monitor is an epoll based infinite loop running in a separate thread that
> waits on channel events from VMs and calls the corresponding functions. Channel
> definitions from the manager are registered via the epoll event opaque pointer
> when calling epoll_ctl(EPOLL_CTL_ADD), this allows for obtaining the channels
> file descriptor for reading EPOLLIN events and mapping the vCPU to pCPU(s)
> associated with a request from a particular VM.
> 
> Signed-off-by: Alan Carew <alan.carew@intel.com>
> Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> ---
>  examples/vm_power_manager/channel_manager.c |  808 +++++++++++++++++++++++++++
>  examples/vm_power_manager/channel_manager.h |  314 +++++++++++
>  examples/vm_power_manager/channel_monitor.c |  234 ++++++++
>  examples/vm_power_manager/channel_monitor.h |  102 ++++
>  4 files changed, 1458 insertions(+), 0 deletions(-)
>  create mode 100644 examples/vm_power_manager/channel_manager.c
>  create mode 100644 examples/vm_power_manager/channel_manager.h
>  create mode 100644 examples/vm_power_manager/channel_monitor.c
>  create mode 100644 examples/vm_power_manager/channel_monitor.h
> 
> diff --git a/examples/vm_power_manager/channel_manager.c b/examples/vm_power_manager/channel_manager.c
> new file mode 100644
> index 0000000..7d744c0
> --- /dev/null
> +++ b/examples/vm_power_manager/channel_manager.c
> @@ -0,0 +1,808 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/un.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <dirent.h>
> +#include <errno.h>
> +
> +#include <sys/queue.h>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <sys/select.h>
> +
> +#include <rte_config.h>
> +#include <rte_malloc.h>
> +#include <rte_memory.h>
> +#include <rte_mempool.h>
> +#include <rte_log.h>
> +#include <rte_atomic.h>
> +#include <rte_spinlock.h>
> +
> +#include <libvirt/libvirt.h>
> +
> +#include "channel_manager.h"
> +#include "channel_commands.h"
> +#include "channel_monitor.h"
> +
> +
> +#define RTE_LOGTYPE_CHANNEL_MANAGER RTE_LOGTYPE_USER1
> +
> +#define ITERATIVE_BITMASK_CHECK_64(mask_u64b, i) \
> +		for (i = 0; mask_u64b; mask_u64b &= ~(1ULL << i++)) \
> +		if ((mask_u64b >> i) & 1) \
> +
You specify masks as 64bit entries throughout this code, is that sufficient?
IIRC someone was just undertaking some work to allow for systems that supported
larger than 64 bit system.  I know linux (and I'm sure bsd) contain a bitmask or
cpumask that is of variable length so that an arbitrary number of cpus can be
specified.

> +/* Global pointer to libvirt connection */
> +static virConnectPtr global_vir_conn_ptr;
> +
> +static unsigned char *global_cpumaps;
> +static virVcpuInfo *global_vircpuinfo;
> +static size_t global_maplen;
> +
> +static unsigned global_n_host_cpus;
> +
> +/*
> + * Represents a single Virtual Machine
> + */
> +struct virtual_machine_info {
> +	char name[CHANNEL_MGR_MAX_NAME_LEN];
> +	rte_atomic64_t pcpu_mask[CHANNEL_CMDS_MAX_CPUS];
> +	struct channel_info *channels[CHANNEL_CMDS_MAX_VM_CHANNELS];
> +	uint64_t channel_mask;
> +	uint8_t num_channels;
> +	enum vm_status status;
> +	virDomainPtr domainPtr;
> +	virDomainInfo info;
> +	rte_spinlock_t config_spinlock;
> +	LIST_ENTRY(virtual_machine_info) vms_info;
> +};
> +
> +LIST_HEAD(, virtual_machine_info) vm_list_head;
> +
> +static struct virtual_machine_info *
> +find_domain_by_name(const char *name)
> +{
> +	struct virtual_machine_info *info;
> +	LIST_FOREACH(info, &vm_list_head, vms_info) {
> +		if (!strncmp(info->name, name, CHANNEL_MGR_MAX_NAME_LEN-1))
> +			return info;
> +	}
> +	return NULL;
> +}
> +
> +static int
> +update_pcpus_mask(struct virtual_machine_info *vm_info)
> +{
> +	virVcpuInfoPtr cpuinfo;
> +	unsigned i, j;
> +	int n_vcpus;
> +	uint64_t mask;
> +
> +	memset(global_cpumaps, 0, CHANNEL_CMDS_MAX_CPUS*global_maplen);
> +
> +	if (!virDomainIsActive(vm_info->domainPtr)) {
> +		n_vcpus = virDomainGetVcpuPinInfo(vm_info->domainPtr,
> +				vm_info->info.nrVirtCpu, global_cpumaps, global_maplen,
> +				VIR_DOMAIN_AFFECT_CONFIG);
> +		if (n_vcpus < 0) {
> +			RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting vCPU info for "
> +					"in-active VM '%s'\n", vm_info->name);
> +			return -1;
> +		}
> +		goto update_pcpus;
> +	}
> +
> +	memset(global_vircpuinfo, 0, sizeof(*global_vircpuinfo)*
> +			CHANNEL_CMDS_MAX_CPUS);
> +
> +	cpuinfo = global_vircpuinfo;
> +
> +	n_vcpus = virDomainGetVcpus(vm_info->domainPtr, cpuinfo,
> +			CHANNEL_CMDS_MAX_CPUS, global_cpumaps, global_maplen);
> +	if (n_vcpus < 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting vCPU info for "
> +				"active VM '%s'\n", vm_info->name);
> +		return -1;
> +	}
> +update_pcpus:
> +	if (n_vcpus >= CHANNEL_CMDS_MAX_CPUS) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Number of vCPUS(%u) is out of range "
> +				"0...%d\n", n_vcpus, CHANNEL_CMDS_MAX_CPUS-1);
> +		return -1;
> +	}
> +	if (n_vcpus != vm_info->info.nrVirtCpu) {
> +		RTE_LOG(INFO, CHANNEL_MANAGER, "Updating the number of vCPUs for VM '%s"
> +				" from %d -> %d\n", vm_info->name, vm_info->info.nrVirtCpu,
> +				n_vcpus);
> +		vm_info->info.nrVirtCpu = n_vcpus;
> +	}
> +	for (i = 0; i < vm_info->info.nrVirtCpu; i++) {
> +		mask = 0;
> +		for (j = 0; j < global_n_host_cpus; j++) {
> +			if (VIR_CPU_USABLE(global_cpumaps, global_maplen, i, j) > 0) {
> +				mask |= 1ULL << j;
> +			}
> +		}
> +		rte_atomic64_set(&vm_info->pcpu_mask[i], mask);
> +	}
> +	return 0;
> +}
> +
> +int
> +set_pcpus_mask(char *vm_name, unsigned vcpu, uint64_t core_mask)
> +{
> +	unsigned i = 0;
> +	int flags = VIR_DOMAIN_AFFECT_LIVE|VIR_DOMAIN_AFFECT_CONFIG;
> +	struct virtual_machine_info *vm_info;
> +	uint64_t mask = core_mask;
> +
> +	if (vcpu >= CHANNEL_CMDS_MAX_CPUS) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "vCPU(%u) exceeds max allowable(%d)\n",
> +				vcpu, CHANNEL_CMDS_MAX_CPUS-1);
> +		return -1;
> +	}
> +
> +	vm_info = find_domain_by_name(vm_name);
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "VM '%s' not found\n", vm_name);
> +		return -1;
> +	}
> +
> +	if (!virDomainIsActive(vm_info->domainPtr)) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to set vCPU(%u) to pCPU "
> +				"mask(0x%"PRIx64") for VM '%s', VM is not active\n",
> +				vcpu, core_mask, vm_info->name);
> +		return -1;
> +	}
> +
> +	if (vcpu >= vm_info->info.nrVirtCpu) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "vCPU(%u) exceeds the assigned number of "
> +				"vCPUs(%u)\n", vcpu, vm_info->info.nrVirtCpu);
> +		return -1;
> +	}
> +	memset(global_cpumaps, 0 , CHANNEL_CMDS_MAX_CPUS * global_maplen);
> +	ITERATIVE_BITMASK_CHECK_64(mask, i) {
> +		VIR_USE_CPU(global_cpumaps, i);
> +		if (i >= global_n_host_cpus) {
> +			RTE_LOG(ERR, CHANNEL_MANAGER, "CPU(%u) exceeds the available "
> +					"number of CPUs(%u)\n", i, global_n_host_cpus);
> +			return -1;
> +		}
> +	}
> +	if (virDomainPinVcpuFlags(vm_info->domainPtr, vcpu, global_cpumaps,
> +			global_maplen, flags) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to set vCPU(%u) to pCPU "
> +				"mask(0x%"PRIx64") for VM '%s'\n", vcpu, core_mask,
> +				vm_info->name);
> +		return -1;
> +	}
> +	rte_atomic64_set(&vm_info->pcpu_mask[vcpu], core_mask);
> +	return 0;
> +
> +}
> +
> +int
> +set_pcpu(char *vm_name, unsigned vcpu, unsigned core_num)
> +{
> +	uint64_t mask = 1ULL << core_num;
> +
> +	return set_pcpus_mask(vm_name, vcpu, mask);
> +}
> +
> +uint64_t
> +get_pcpus_mask(struct channel_info *chan_info, unsigned vcpu)
> +{
> +	struct virtual_machine_info *vm_info =
> +			(struct virtual_machine_info *)chan_info->priv_info;
> +	return rte_atomic64_read(&vm_info->pcpu_mask[vcpu]);
> +}
> +
> +static inline int
> +channel_exists(struct virtual_machine_info *vm_info, unsigned channel_num)
Is your intent for this to always be inlined?  If so, you likely meant to make
this __always_inline__ (or whatever the unilateral inline macro is)

> +{
> +	rte_spinlock_lock(&(vm_info->config_spinlock));
> +	if (vm_info->channel_mask & (1ULL << channel_num)) {
> +		rte_spinlock_unlock(&(vm_info->config_spinlock));
> +		return 1;
> +	}
> +	rte_spinlock_unlock(&(vm_info->config_spinlock));
> +	return 0;
> +}
> +
> +
> +
> +static int
> +open_non_blocking_channel(struct channel_info *info)
> +{
> +	int ret, flags;
> +	struct sockaddr_un sock_addr;
> +	fd_set soc_fd_set;
> +	struct timeval tv;
> +
> +	info->fd = socket(AF_UNIX, SOCK_STREAM, 0);
> +	if (info->fd == -1) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error(%s) creating socket for '%s'\n",
> +				strerror(errno),
> +				info->channel_path);
> +		return -1;
> +	}
> +	sock_addr.sun_family = AF_UNIX;
> +	memcpy(&sock_addr.sun_path, info->channel_path,
> +			strlen(info->channel_path)+1);
> +
> +	/* Get current flags */
> +	flags = fcntl(info->fd, F_GETFL, 0);
> +	if (flags < 0) {
> +		RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) fcntl get flags socket for"
> +				"'%s'\n", strerror(errno), info->channel_path);
> +		return 1;
> +	}
> +	/* Set to Non Blocking */
> +	flags |= O_NONBLOCK;
> +	if (fcntl(info->fd, F_SETFL, flags) < 0) {
> +		RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) setting non-blocking "
> +				"socket for '%s'\n", strerror(errno), info->channel_path);
> +		return -1;
> +	}
> +	ret = connect(info->fd, (struct sockaddr *)&sock_addr,
> +			sizeof(sock_addr));
> +	if (ret < 0) {
> +		/* ECONNREFUSED error is given when VM is not active */
> +		if (errno == ECONNREFUSED) {
> +			RTE_LOG(WARNING, CHANNEL_MANAGER, "VM is not active or has not "
> +					"activated its endpoint to channel %s\n",
> +					info->channel_path);
> +			return -1;
> +		}
> +		/* Wait for tv_sec if in progress */
> +		else if (errno == EINPROGRESS) {
> +			tv.tv_sec = 2;
> +			tv.tv_usec = 0;
> +			FD_ZERO(&soc_fd_set);
> +			FD_SET(info->fd, &soc_fd_set);
> +			if (select(info->fd+1, NULL, &soc_fd_set, NULL, &tv) > 0) {
> +				RTE_LOG(WARNING, CHANNEL_MANAGER, "Timeout or error on channel "
> +						"'%s'\n", info->channel_path);
> +				return -1;
> +			}
> +		} else {
> +			/* Any other error */
> +			RTE_LOG(WARNING, CHANNEL_MANAGER, "Error(%s) connecting socket"
> +					" for '%s'\n", strerror(errno), info->channel_path);
> +			return -1;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int
> +setup_channel_info(struct virtual_machine_info **vm_info_dptr,
> +		struct channel_info **chan_info_dptr, unsigned channel_num)
> +{
> +	struct channel_info *chan_info = *chan_info_dptr;
> +	struct virtual_machine_info *vm_info = *vm_info_dptr;
> +
> +	chan_info->channel_num = channel_num;
> +	chan_info->priv_info = (void *)vm_info;
> +	chan_info->status = CHANNEL_MGR_CHANNEL_DISCONNECTED;
> +	if (open_non_blocking_channel(chan_info) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Could not open channel: "
> +				"'%s' for VM '%s'\n",
> +				chan_info->channel_path, vm_info->name);
> +		return -1;
> +	}
> +	if (add_channel_to_monitor(&chan_info) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Could add channel: "
> +				"'%s' to epoll ctl for VM '%s'\n",
> +				chan_info->channel_path, vm_info->name);
> +		return -1;
> +
> +	}
> +	rte_spinlock_lock(&(vm_info->config_spinlock));
> +	vm_info->num_channels++;
> +	vm_info->channel_mask |= 1ULL << channel_num;
> +	vm_info->channels[channel_num] = chan_info;
> +	chan_info->status = CHANNEL_MGR_CHANNEL_CONNECTED;
> +	rte_spinlock_unlock(&(vm_info->config_spinlock));
> +	return 0;
> +}
> +
> +int
> +add_all_channels(const char *vm_name)
> +{
> +	DIR *d;
> +	struct dirent *dir;
> +	struct virtual_machine_info *vm_info;
> +	struct channel_info *chan_info;
> +	char *token, *remaining, *tail_ptr;
> +	char socket_name[PATH_MAX];
> +	unsigned channel_num;
> +	int num_channels_enabled = 0;
> +
> +	/* verify VM exists */
> +	vm_info = find_domain_by_name(vm_name);
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' not found"
> +				" during channel discovery\n", vm_name);
> +		return 0;
> +	}
> +	if (!virDomainIsActive(vm_info->domainPtr)) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' is not active\n", vm_name);
> +		vm_info->status = CHANNEL_MGR_VM_INACTIVE;
> +		return 0;
> +	}
> +	d = opendir(CHANNEL_MGR_SOCKET_PATH);
> +	if (d == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error opening directory '%s': %s\n",
> +				CHANNEL_MGR_SOCKET_PATH, strerror(errno));
> +		return -1;
> +	}
> +	while ((dir = readdir(d)) != NULL) {
> +		if (!strncmp(dir->d_name, ".", 1) ||
> +				!strncmp(dir->d_name, "..", 2))
> +			continue;
> +
> +		snprintf(socket_name, sizeof(socket_name), "%s", dir->d_name);
> +		remaining = socket_name;
> +		/* Extract vm_name from "<vm_name>.<channel_num>" */
> +		token = strsep(&remaining, ".");
> +		if (remaining == NULL)
> +			continue;
> +		if (strncmp(vm_name, token, CHANNEL_MGR_MAX_NAME_LEN))
> +			continue;
> +
> +		/* remaining should contain only <channel_num> */
> +		errno = 0;
> +		channel_num = (unsigned)strtol(remaining, &tail_ptr, 0);
> +		if ((errno != 0) || (remaining[0] == '\0') ||
> +				(*tail_ptr != '\0') || tail_ptr == NULL) {
> +			RTE_LOG(WARNING, CHANNEL_MANAGER, "Malformed channel name"
> +					"'%s' found it should be in the form of "
> +					"'<guest_name>.<channel_num>(decimal)'\n",
> +					dir->d_name);
> +			continue;
> +		}
> +		if (channel_num >= CHANNEL_CMDS_MAX_VM_CHANNELS) {
> +			RTE_LOG(WARNING, CHANNEL_MANAGER, "Channel number(%u) is "
> +					"greater than max allowable: %d, skipping '%s%s'\n",
> +					channel_num, CHANNEL_CMDS_MAX_VM_CHANNELS-1,
> +					CHANNEL_MGR_SOCKET_PATH, dir->d_name);
> +			continue;
> +		}
> +		/* if channel has not been added previously */
> +		if (channel_exists(vm_info, channel_num))
> +			continue;
> +
> +		chan_info = rte_malloc(NULL, sizeof(*chan_info),
> +				CACHE_LINE_SIZE);
> +		if (chan_info == NULL) {
> +			RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
> +				"channel '%s%s'\n", CHANNEL_MGR_SOCKET_PATH, dir->d_name);
> +			continue;
> +		}
> +
> +		snprintf(chan_info->channel_path,
> +				sizeof(chan_info->channel_path), "%s%s",
> +				CHANNEL_MGR_SOCKET_PATH, dir->d_name);
> +
> +		if (setup_channel_info(&vm_info, &chan_info, channel_num) < 0) {
> +			rte_free(chan_info);
> +			continue;
> +		}
> +
> +		num_channels_enabled++;
> +	}
> +	closedir(d);
> +	return num_channels_enabled;
> +}
> +
> +int
> +add_channels(const char *vm_name, unsigned *channel_list,
> +		unsigned len_channel_list)
> +{
> +	struct virtual_machine_info *vm_info;
> +	struct channel_info *chan_info;
> +	char socket_path[PATH_MAX];
> +	unsigned i;
> +	int num_channels_enabled = 0;
> +
> +	vm_info = find_domain_by_name(vm_name);
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add channels: VM '%s' "
> +				"not found\n", vm_name);
> +		return 0;
> +	}
> +
> +	if (!virDomainIsActive(vm_info->domainPtr)) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "VM: '%s' is not active\n", vm_name);
> +		vm_info->status = CHANNEL_MGR_VM_INACTIVE;
> +		return 0;
> +	}
> +
> +	for (i = 0; i < len_channel_list; i++) {
> +
> +		if (channel_list[i] >= CHANNEL_CMDS_MAX_VM_CHANNELS) {
> +			RTE_LOG(INFO, CHANNEL_MANAGER, "Channel(%u) is out of range "
> +							"0...%d\n", channel_list[i],
> +							CHANNEL_CMDS_MAX_VM_CHANNELS-1);
> +			continue;
> +		}
> +		if (channel_exists(vm_info, channel_list[i])) {
> +			RTE_LOG(INFO, CHANNEL_MANAGER, "Channel already exists, skipping  "
> +					"'%s.%u'\n", vm_name, i);
> +			continue;
> +		}
> +
> +		snprintf(socket_path, sizeof(socket_path), "%s%s.%u",
> +				CHANNEL_MGR_SOCKET_PATH, vm_name, channel_list[i]);
> +		errno = 0;
> +		if (access(socket_path, F_OK) < 0) {
> +			RTE_LOG(ERR, CHANNEL_MANAGER, "Channel path '%s' error: "
> +					"%s\n", socket_path, strerror(errno));
> +			continue;
> +		}
> +		chan_info = rte_malloc(NULL, sizeof(*chan_info),
> +				CACHE_LINE_SIZE);
> +		if (chan_info == NULL) {
> +			RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for "
> +					"channel '%s'\n", socket_path);
> +			continue;
> +		}
> +		snprintf(chan_info->channel_path,
> +				sizeof(chan_info->channel_path), "%s%s.%u",
> +				CHANNEL_MGR_SOCKET_PATH, vm_name, channel_list[i]);
> +		if (setup_channel_info(&vm_info, &chan_info, channel_list[i]) < 0) {
> +			rte_free(chan_info);
> +			continue;
> +		}
> +		num_channels_enabled++;
> +
> +	}
> +	return num_channels_enabled;
> +}
> +
> +int
> +remove_channel(struct channel_info **chan_info_dptr)
> +{
> +	struct virtual_machine_info *vm_info;
> +	struct channel_info *chan_info = *chan_info_dptr;
> +
> +	close(chan_info->fd);
> +
> +	vm_info = (struct virtual_machine_info *)chan_info->priv_info;
> +
> +	rte_spinlock_lock(&(vm_info->config_spinlock));
> +	vm_info->channel_mask &= ~(1ULL << chan_info->channel_num);
> +	vm_info->num_channels--;
> +	rte_spinlock_unlock(&(vm_info->config_spinlock));
> +
> +	rte_free(chan_info);
> +	return 0;
> +}
> +
> +int
> +set_channel_status_all(const char *vm_name, enum channel_status status)
> +{
> +	struct virtual_machine_info *vm_info;
> +	unsigned i;
> +	uint64_t mask;
> +	int num_channels_changed = 0;
> +
> +	if (!(status == CHANNEL_MGR_CHANNEL_CONNECTED ||
> +			status == CHANNEL_MGR_CHANNEL_DISABLED)) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Channels can only be enabled or "
> +				"disabled: Unable to change status for VM '%s'\n", vm_name);
> +	}
> +	vm_info = find_domain_by_name(vm_name);
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to disable channels: VM '%s' "
> +				"not found\n", vm_name);
> +		return 0;
> +	}
> +
> +	rte_spinlock_lock(&(vm_info->config_spinlock));
> +	mask = vm_info->channel_mask;
> +	ITERATIVE_BITMASK_CHECK_64(mask, i) {
> +		vm_info->channels[i]->status = status;
> +		num_channels_changed++;
> +	}
> +	rte_spinlock_unlock(&(vm_info->config_spinlock));
> +	return num_channels_changed;
> +
> +}
> +
> +int
> +set_channel_status(const char *vm_name, unsigned *channel_list,
> +		unsigned len_channel_list, enum channel_status status)
> +{
> +	struct virtual_machine_info *vm_info;
> +	unsigned i;
> +	int num_channels_changed = 0;
> +
> +	if (!(status == CHANNEL_MGR_CHANNEL_CONNECTED ||
> +			status == CHANNEL_MGR_CHANNEL_DISABLED)) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Channels can only be enabled or "
> +				"disabled: Unable to change status for VM '%s'\n", vm_name);
> +	}
> +	vm_info = find_domain_by_name(vm_name);
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add channels: VM '%s' "
> +				"not found\n", vm_name);
> +		return 0;
> +	}
> +	for (i = 0; i < len_channel_list; i++) {
> +		if (channel_exists(vm_info, channel_list[i])) {
> +			rte_spinlock_lock(&(vm_info->config_spinlock));
> +			vm_info->channels[channel_list[i]]->status = status;
> +			rte_spinlock_unlock(&(vm_info->config_spinlock));
> +			num_channels_changed++;
> +		}
> +	}
> +	return num_channels_changed;
> +}
> +
> +int
> +get_info_vm(const char *vm_name, struct vm_info *info)
> +{
> +	struct virtual_machine_info *vm_info;
> +	unsigned i, channel_num = 0;
> +	uint64_t mask;
> +
> +	vm_info = find_domain_by_name(vm_name);
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "VM '%s' not found\n", vm_name);
> +		return -1;
> +	}
> +	info->status = CHANNEL_MGR_VM_ACTIVE;
> +	if (!virDomainIsActive(vm_info->domainPtr))
> +		info->status = CHANNEL_MGR_VM_INACTIVE;
> +
> +	rte_spinlock_lock(&(vm_info->config_spinlock));
> +
> +	mask = vm_info->channel_mask;
> +	ITERATIVE_BITMASK_CHECK_64(mask, i) {
> +		info->channels[channel_num].channel_num = i;
> +		memcpy(info->channels[channel_num].channel_path,
> +				vm_info->channels[i]->channel_path, PATH_MAX);
> +		info->channels[channel_num].status = vm_info->channels[i]->status;
> +		info->channels[channel_num].fd = vm_info->channels[i]->fd;
> +		channel_num++;
> +	}
> +
> +	info->num_channels = channel_num;
> +	info->num_vcpus = vm_info->info.nrVirtCpu;
> +	rte_spinlock_unlock(&(vm_info->config_spinlock));
> +
> +	memcpy(info->name, vm_info->name, sizeof(vm_info->name));
> +	for (i = 0; i < info->num_vcpus; i++) {
> +		info->pcpu_mask[i] = rte_atomic64_read(&vm_info->pcpu_mask[i]);
> +	}
> +	return 0;
> +}
> +
> +int
> +add_vm(const char *vm_name)
> +{
> +	struct virtual_machine_info *new_domain;
> +	virDomainPtr dom_ptr;
> +	int i;
> +
> +	if (find_domain_by_name(vm_name) != NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to add VM: VM '%s' "
> +				"already exists\n", vm_name);
> +		return -1;
> +	}
> +
> +	if (global_vir_conn_ptr == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "No connection to hypervisor exists\n");
> +		return -1;
> +	}
> +	dom_ptr = virDomainLookupByName(global_vir_conn_ptr, vm_name);
> +	if (dom_ptr == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error on VM lookup with libvirt: "
> +				"VM '%s' not found\n", vm_name);
> +		return -1;
> +	}
> +
> +	new_domain = rte_malloc("virtual_machine_info", sizeof(*new_domain),
> +			CACHE_LINE_SIZE);
> +	if (new_domain == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to allocate memory for VM "
> +				"info\n");
> +		return -1;
> +	}
> +	new_domain->domainPtr = dom_ptr;
> +	if (virDomainGetInfo(new_domain->domainPtr, &new_domain->info) != 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to get libvirt VM info\n");
> +		rte_free(new_domain);
> +		return -1;
> +	}
> +	if (new_domain->info.nrVirtCpu > CHANNEL_CMDS_MAX_CPUS) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error the number of virtual CPUs(%u) is "
> +				"greater than allowable(%d)\n", new_domain->info.nrVirtCpu,
> +				CHANNEL_CMDS_MAX_CPUS);
> +		rte_free(new_domain);
> +		return -1;
> +	}
> +
> +	for (i = 0; i < CHANNEL_CMDS_MAX_CPUS; i++) {
> +		rte_atomic64_init(&new_domain->pcpu_mask[i]);
> +	}
> +	if (update_pcpus_mask(new_domain) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error getting physical CPU pinning\n");
> +		rte_free(new_domain);
> +		return -1;
> +	}
> +	strncpy(new_domain->name, vm_name, sizeof(new_domain->name));
> +	new_domain->channel_mask = 0;
> +	new_domain->num_channels = 0;
> +
> +	if (!virDomainIsActive(dom_ptr))
> +		new_domain->status = CHANNEL_MGR_VM_INACTIVE;
> +	else
> +		new_domain->status = CHANNEL_MGR_VM_ACTIVE;
> +
> +	rte_spinlock_init(&(new_domain->config_spinlock));
> +	LIST_INSERT_HEAD(&vm_list_head, new_domain, vms_info);
> +	return 0;
> +}
> +
> +int
> +remove_vm(const char *vm_name)
> +{
> +	struct virtual_machine_info *vm_info = find_domain_by_name(vm_name);
> +
> +	if (vm_info == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to remove VM: VM '%s' "
> +				"not found\n", vm_name);
> +		return -1;
> +	}
> +	rte_spinlock_lock(&vm_info->config_spinlock);
> +	if (vm_info->num_channels != 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to remove VM '%s', there are "
> +				"%"PRId8" channels still active\n",
> +				vm_name, vm_info->num_channels);
> +		rte_spinlock_unlock(&vm_info->config_spinlock);
> +		return -1;
> +	}
> +	LIST_REMOVE(vm_info, vms_info);
> +	rte_spinlock_unlock(&vm_info->config_spinlock);
> +	rte_free(vm_info);
> +	return 0;
> +}
> +
> +static void
> +disconnect_hypervisor(void)
> +{
> +	if (global_vir_conn_ptr != NULL) {
> +		virConnectClose(global_vir_conn_ptr);
> +		global_vir_conn_ptr = NULL;
> +	}
> +}
> +
> +static int
> +connect_hypervisor(const char *path)
> +{
> +	if (global_vir_conn_ptr != NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error connecting to %s, connection"
> +				"already established\n", path);
> +		return -1;
> +	}
> +	global_vir_conn_ptr = virConnectOpen(path);
> +	if (global_vir_conn_ptr == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error failed to open connection to "
> +				"Hypervisor '%s'\n", path);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +int
> +channel_manager_init(const char *path)
> +{
> +	int n_cpus;
> +
> +	LIST_INIT(&vm_list_head);
> +	if (connect_hypervisor(path) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to initialize channel manager\n");
> +		return -1;
> +	}
> +
> +	global_maplen = VIR_CPU_MAPLEN(CHANNEL_CMDS_MAX_CPUS);
> +
> +	global_vircpuinfo = rte_zmalloc(NULL, sizeof(*global_vircpuinfo) *
> +			CHANNEL_CMDS_MAX_CPUS, CACHE_LINE_SIZE);
> +	if (global_vircpuinfo == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Error allocating memory for CPU Info\n");
> +		goto error;
> +	}
> +	global_cpumaps = rte_zmalloc(NULL, CHANNEL_CMDS_MAX_CPUS * global_maplen,
> +			CACHE_LINE_SIZE);
> +	if (global_cpumaps == NULL) {
> +		goto error;
> +	}
> +
> +	n_cpus = virNodeGetCPUMap(global_vir_conn_ptr, NULL, NULL, 0);
> +	if (n_cpus <= 0) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "Unable to get the number of Host "
> +				"CPUs\n");
> +		goto error;
> +	}
> +	global_n_host_cpus = (unsigned)n_cpus;
> +
> +	if (global_n_host_cpus > CHANNEL_CMDS_MAX_CPUS) {
> +		RTE_LOG(ERR, CHANNEL_MANAGER, "The number of host CPUs(%u) exceeds the "
> +				"maximum of %u\n", global_n_host_cpus, CHANNEL_CMDS_MAX_CPUS);
> +		goto error;
> +
> +	}
> +
> +	return 0;
> +error:
> +	disconnect_hypervisor();
> +	return -1;
> +}
> +
> +void
> +channel_manager_exit(void)
> +{
> +	unsigned i;
> +	uint64_t mask;
> +	struct virtual_machine_info *vm_info;
> +
> +	LIST_FOREACH(vm_info, &vm_list_head, vms_info) {
> +
> +		rte_spinlock_lock(&(vm_info->config_spinlock));
> +
> +		mask = vm_info->channel_mask;
> +		ITERATIVE_BITMASK_CHECK_64(mask, i) {
> +			remove_channel_from_monitor(vm_info->channels[i]);
> +			close(vm_info->channels[i]->fd);
> +			rte_free(vm_info->channels[i]);
> +		}
> +		rte_spinlock_unlock(&(vm_info->config_spinlock));
> +
> +		LIST_REMOVE(vm_info, vms_info);
> +		rte_free(vm_info);
> +	}
> +
> +	if (global_cpumaps != NULL)
> +		rte_free(global_cpumaps);
> +	if (global_vircpuinfo != NULL)
> +		rte_free(global_vircpuinfo);
> +	disconnect_hypervisor();
> +}
> diff --git a/examples/vm_power_manager/channel_manager.h b/examples/vm_power_manager/channel_manager.h
> new file mode 100644
> index 0000000..12c29c3
> --- /dev/null
> +++ b/examples/vm_power_manager/channel_manager.h
> @@ -0,0 +1,314 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef CHANNEL_MANAGER_H_
> +#define CHANNEL_MANAGER_H_
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <linux/limits.h>
> +#include <rte_atomic.h>
> +#include "channel_commands.h"
> +
> +/* Maximum name length including '\0' terminator */
> +#define CHANNEL_MGR_MAX_NAME_LEN    64
> +
> +/* Maximum number of channels to each Virtual Machine */
> +#define CHANNEL_MGR_MAX_CHANNELS    64
> +
> +/* Hypervisor Path for libvirt(qemu/KVM) */
> +#define CHANNEL_MGR_DEFAULT_HV_PATH "qemu:///system"
> +
> +/* File socket directory */
> +#define CHANNEL_MGR_SOCKET_PATH     "/tmp/powermonitor/"
> +
> +/* Communication Channel Status */
> +enum channel_status { CHANNEL_MGR_CHANNEL_DISCONNECTED = 0,
> +	CHANNEL_MGR_CHANNEL_CONNECTED,
> +	CHANNEL_MGR_CHANNEL_DISABLED,
> +	CHANNEL_MGR_CHANNEL_PROCESSING};
> +
> +/* VM libvirt(qemu/KVM) connection status */
> +enum vm_status { CHANNEL_MGR_VM_INACTIVE = 0, CHANNEL_MGR_VM_ACTIVE};
> +
> +/*
> + *  Represents a single and exclusive VM channel that exists between a guest and
> + *  the host.
> + */
> +struct channel_info {
> +	char channel_path[PATH_MAX]; /**< Path to host socket */
> +	volatile uint32_t status;    /**< Connection status(enum channel_status) */
> +	int fd;                      /**< AF_UNIX socket fd */
> +	unsigned channel_num;        /**< CHANNEL_MGR_SOCKET_PATH/<vm_name>.channel_num */
> +	void *priv_info;             /**< Pointer to private info, do not modify */
> +};
> +
> +/* Represents a single VM instance used to return internal information about
> + * a VM */
> +struct vm_info {
> +	char name[CHANNEL_MGR_MAX_NAME_LEN];          /**< VM name */
> +	enum vm_status status;                        /**< libvirt status */
> +	uint64_t pcpu_mask[CHANNEL_CMDS_MAX_CPUS];    /**< pCPU mask for each vCPU */
> +	unsigned num_vcpus;                           /**< number of vCPUS */
> +	struct channel_info channels[CHANNEL_MGR_MAX_CHANNELS]; /**< Array of channel_info */
> +	unsigned num_channels;                        /**< Number of channels */
> +};
> +
> +/**
> + * Initialize the Channel Manager resources and connect to the Hypervisor
> + * specified in path.
> + * This must be successfully called first before calling any other functions.
> + * It must only be call once;
> + *
> + * @param path
> + *  Must be a local path, e.g. qemu:///system.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int channel_manager_init(const char *path);
> +
> +/**
> + * Free resources associated with the Channel Manager.
> + *
> + * @param path
> + *  Must be a local path, e.g. qemu:///system.
> + *
> + * @return
> + *  None
> + */
> +void channel_manager_exit(void);
> +
> +/**
> + * Get the Physical CPU mask for VM lcore channel(vcpu), result is assigned to
> + * core_mask.
> + * It is not thread-safe.
> + *
> + * @param chan_info
> + *  Pointer to struct channel_info
> + *
> + * @param vcpu
> + *  The virtual CPU to query.
> + *
> + *
> + * @return
> + *  - 0 on error.
> + *  - >0 on success.
> + */
> +uint64_t get_pcpus_mask(struct channel_info *chan_info, unsigned vcpu);
> +
> +/**
> + * Set the Physical CPU mask for the specified vCPU.
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to lookup
> + *
> + * @param vcpu
> + *  The virtual CPU to set.
> + *
> + * @param core_mask
> + *  The core mask of the physical CPU(s) to bind the vCPU
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int set_pcpus_mask(char *vm_name, unsigned vcpu, uint64_t core_mask);
> +
> +/**
> + * Set the Physical CPU for the specified vCPU.
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to lookup
> + *
> + * @param vcpu
> + *  The virtual CPU to set.
> + *
> + * @param core_num
> + *  The core number of the physical CPU(s) to bind the vCPU
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int set_pcpu(char *vm_name, unsigned vcpu, unsigned core_num);
> +/**
> + * Add a VM as specified by name to the Channel Manager. The name must
> + * correspond to a valid libvirt domain name.
> + * This is required prior to adding channels.
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to lookup.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int add_vm(const char *name);
> +
> +/**
> + * Remove a previously added Virtual Machine from the Channel Manager
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to lookup.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int remove_vm(const char *name);
> +
> +/**
> + * Add all available channels to the VM as specified by name.
> + * Channels in the form of paths
> + * (CHANNEL_MGR_SOCKET_PATH/<vm_name>.<channel_number>) will only be parsed.
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to lookup.
> + *
> + * @return
> + *  - N the number of channels added for the VM
> + */
> +int add_all_channels(const char *vm_name);
> +
> +/**
> + * Add the channel numbers in channel_list to the domain specified by name.
> + * Channels in the form of paths
> + * (CHANNEL_MGR_SOCKET_PATH/<vm_name>.<channel_number>) will only be parsed.
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to add channels.
> + *
> + * @param channel_list
> + *  Pointer to list of unsigned integers, representing the channel number to add
> + *  It must be allocated outside of this function.
> + *
> + * @param num_channels
> + *  The amount of channel numbers in channel_list
> + *
> + * @return
> + *  - N the number of channels added for the VM
> + *  - 0 for error
> + */
> +int add_channels(const char *vm_name, unsigned *channel_list,
> +		unsigned num_channels);
> +
> +/**
> + * Remove a channel definition from the channel manager. This must only be
> + * called from the channel monitor thread.
> + *
> + * @param chan_info
> + *  Pointer to a valid struct channel_info.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int remove_channel(struct channel_info **chan_info_dptr);
> +
> +/**
> + * For all channels associated with a Virtual Machine name, update the
> + * connection status. Valid states are CHANNEL_MGR_CHANNEL_CONNECTED or
> + * CHANNEL_MGR_CHANNEL_DISABLED only.
> + *
> + *
> + * @param name
> + *  Virtual Machine name to modify all channels.
> + *
> + * @param status
> + *  The status to set each channel
> + *
> + * @param num_channels
> + *  The amount of channel numbers in channel_list
> + *
> + * @return
> + *  - N the number of channels added for the VM
> + *  - 0 for error
> + */
> +int set_channel_status_all(const char *name, enum channel_status status);
> +
> +/**
> + * For all channels in channel_list associated with a Virtual Machine name
> + * update the connection status of each.
> + * Valid states are CHANNEL_MGR_CHANNEL_CONNECTED or
> + * CHANNEL_MGR_CHANNEL_DISABLED only.
> + * It is not thread-safe.
> + *
> + * @param name
> + *  Virtual Machine name to add channels.
> + *
> + * @param channel_list
> + *  Pointer to list of unsigned integers, representing the channel numbers to
> + *  modify.
> + *  It must be allocated outside of this function.
> + *
> + * @param num_channels
> + *  The amount of channel numbers in channel_list
> + *
> + * @return
> + *  - N the number of channels modified for the VM
> + *  - 0 for error
> + */
> +int set_channel_status(const char *vm_name, unsigned *channel_list,
> +		unsigned len_channel_list, enum channel_status status);
> +
> +/**
> + * Populates a pointer to struct vm_info associated with vm_name.
> + *
> + * @param vm_name
> + *  The name of the virtual machine to lookup.
> + *
> + *  @param vm_info
> + *   Pointer to a struct vm_info, this must be allocated prior to calling this
> + *   function.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int get_info_vm(const char *vm_name, struct vm_info *info);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* CHANNEL_MANAGER_H_ */
> diff --git a/examples/vm_power_manager/channel_monitor.c b/examples/vm_power_manager/channel_monitor.c
> new file mode 100644
> index 0000000..e3c1b0c
> --- /dev/null
> +++ b/examples/vm_power_manager/channel_monitor.c
> @@ -0,0 +1,234 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <signal.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/epoll.h>
> +#include <sys/queue.h>
> +
> +#include <rte_config.h>
> +#include <rte_log.h>
> +#include <rte_memory.h>
> +#include <rte_malloc.h>
> +#include <rte_atomic.h>
> +
> +
> +#include "channel_monitor.h"
> +#include "channel_commands.h"
> +#include "channel_manager.h"
> +#include "power_manager.h"
> +
> +#define RTE_LOGTYPE_CHANNEL_MONITOR RTE_LOGTYPE_USER1
> +
> +#define MAX_EVENTS 256
> +
> +
> +static volatile unsigned run_loop = 1;
> +static int global_event_fd;
> +static struct epoll_event *global_events_list;
> +
> +void channel_monitor_exit(void)
> +{
> +	run_loop = 0;
> +	rte_free(global_events_list);
> +}
> +
> +static int
> +process_request(struct channel_packet *pkt, struct channel_info *chan_info)
> +{
> +	uint64_t core_mask;
> +
> +	if (chan_info == NULL)
> +		return -1;
> +
> +	if (rte_atomic32_cmpset(&(chan_info->status), CHANNEL_MGR_CHANNEL_CONNECTED,
> +			CHANNEL_MGR_CHANNEL_PROCESSING) == 0)
> +		return -1;
> +
> +	if (pkt->command == CPU_POWER) {
> +		core_mask = get_pcpus_mask(chan_info, pkt->resource_id);
> +		if (core_mask == 0) {
> +			RTE_LOG(ERR, CHANNEL_MONITOR, "Error get physical CPU mask for "
> +				"channel '%s' using vCPU(%u)\n", chan_info->channel_path,
> +				(unsigned)pkt->unit);
> +			return -1;
> +		}
> +		if (__builtin_popcountll(core_mask) == 1) {
> +
> +			unsigned core_num = __builtin_ffsll(core_mask) - 1;
> +
> +			switch (pkt->unit) {
> +			case(CPU_POWER_SCALE_MIN):
> +					power_manager_scale_core_min(core_num);
> +			break;
> +			case(CPU_POWER_SCALE_MAX):
> +					power_manager_scale_core_max(core_num);
> +			break;
> +			case(CPU_POWER_SCALE_DOWN):
> +					power_manager_scale_core_down(core_num);
> +			break;
> +			case(CPU_POWER_SCALE_UP):
> +					power_manager_scale_core_up(core_num);
> +			break;
> +			default:
> +				break;
> +			}
> +		} else {
> +			switch (pkt->unit) {
> +			case(CPU_POWER_SCALE_MIN):
> +					power_manager_scale_mask_min(core_mask);
> +			break;
> +			case(CPU_POWER_SCALE_MAX):
> +					power_manager_scale_mask_max(core_mask);
> +			break;
> +			case(CPU_POWER_SCALE_DOWN):
> +					power_manager_scale_mask_down(core_mask);
> +			break;
> +			case(CPU_POWER_SCALE_UP):
> +					power_manager_scale_mask_up(core_mask);
> +			break;
> +			default:
> +				break;
> +			}
> +
> +		}
> +	}
> +	/* Return is not checked as channel status may have been set to DISABLED
> +	 * from management thread
> +	 */
> +	rte_atomic32_cmpset(&(chan_info->status), CHANNEL_MGR_CHANNEL_PROCESSING,
> +			CHANNEL_MGR_CHANNEL_CONNECTED);
> +	return 0;
> +
> +}
> +
> +int
> +add_channel_to_monitor(struct channel_info **chan_info)
> +{
> +	struct channel_info *info = *chan_info;
> +	struct epoll_event event;
> +
> +	event.events = EPOLLIN;
> +	event.data.ptr = info;
> +	if (epoll_ctl(global_event_fd, EPOLL_CTL_ADD, info->fd, &event) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MONITOR, "Unable to add channel '%s' "
> +				"to epoll\n", info->channel_path);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +int
> +remove_channel_from_monitor(struct channel_info *chan_info)
> +{
> +	if (epoll_ctl(global_event_fd, EPOLL_CTL_DEL, chan_info->fd, NULL) < 0) {
> +		RTE_LOG(ERR, CHANNEL_MONITOR, "Unable to remove channel '%s' "
> +				"from epoll\n", chan_info->channel_path);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +int
> +channel_monitor_init(void)
> +{
> +	global_event_fd = epoll_create1(0);
> +	if (global_event_fd == 0) {
> +		RTE_LOG(ERR, CHANNEL_MONITOR, "Error creating epoll context with "
> +				"error %s\n", strerror(errno));
> +		return -1;
> +	}
> +	global_events_list = rte_malloc("epoll_events", sizeof(*global_events_list)
> +			* MAX_EVENTS, CACHE_LINE_SIZE);
> +	if (global_events_list == NULL) {
> +		RTE_LOG(ERR, CHANNEL_MONITOR, "Unable to rte_malloc for"
> +				"epoll events\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +void
> +run_channel_monitor(void)
> +{
> +	while (run_loop) {
> +		int n_events, i;
> +
> +		n_events = epoll_wait(global_event_fd, global_events_list,
> +				MAX_EVENTS, 1);
> +		if (!run_loop)
> +			break;
> +		for (i = 0; i < n_events; i++) {
> +			struct channel_info *chan_info = (struct channel_info *)
> +					global_events_list[i].data.ptr;
> +			if ((global_events_list[i].events & EPOLLERR) ||
> +					(global_events_list[i].events & EPOLLHUP)) {
> +				RTE_LOG(DEBUG, CHANNEL_MONITOR, "Remote closed connection for "
> +						"channel '%s'\n", chan_info->channel_path);
> +				remove_channel(&chan_info);
> +				continue;
> +			}
> +			if (global_events_list[i].events & EPOLLIN) {
> +
> +				int n_bytes, err = 0;
> +				struct channel_packet pkt;
> +				void *buffer = &pkt;
> +				int buffer_len = sizeof(pkt);
> +
> +				while (buffer_len > 0) {
> +					n_bytes = read(chan_info->fd, buffer, buffer_len);
> +					if (n_bytes == buffer_len)
> +						break;
> +					if (n_bytes == -1) {
> +						err = errno;
> +						RTE_LOG(DEBUG, CHANNEL_MONITOR, "Received error on "
> +								"channel '%s' read: %s\n",
> +								chan_info->channel_path, strerror(err));
> +						remove_channel(&chan_info);
> +						break;
> +					}
> +					buffer = (char *)buffer + n_bytes;
> +					buffer_len -= n_bytes;
> +				}
> +				if (!err)
> +					process_request(&pkt, chan_info);
> +			}
> +		}
> +	}
> +}
> diff --git a/examples/vm_power_manager/channel_monitor.h b/examples/vm_power_manager/channel_monitor.h
> new file mode 100644
> index 0000000..c138607
> --- /dev/null
> +++ b/examples/vm_power_manager/channel_monitor.h
> @@ -0,0 +1,102 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef CHANNEL_MONITOR_H_
> +#define CHANNEL_MONITOR_H_
> +
> +#include "channel_manager.h"
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * Setup the Channel Monitor resources required to initialize epoll.
> + * Must be called first before calling other functions.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int channel_monitor_init(void);
> +
> +/**
> + * Run the channel monitor, loops forever on on epoll_wait.
> + *
> + *
> + * @return
> + *  None
> + */
> +void run_channel_monitor(void);
> +
> +/**
> + * Exit the Channel Monitor, exiting the epoll_wait loop and events processing.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +void channel_monitor_exit(void);
> +
> +/**
> + * Add an open channel to monitor via epoll. A pointer to struct channel_info
> + * will be registered with epoll for event processing.
> + * It is thread-safe.
> + *
> + * @param chan_info
> + *  Pointer to struct channel_info pointer.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int add_channel_to_monitor(struct channel_info **chan_info);
> +
> +/**
> + * Remove a previously added channel from epoll control.
> + *
> + * @param chan_info
> + *  Pointer to struct channel_info.
> + *
> + * @return
> + *  - 0 on success.
> + *  - Negative on error.
> + */
> +int remove_channel_from_monitor(struct channel_info *chan_info);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +
> +#endif /* CHANNEL_MONITOR_H_ */
> -- 
> 1.7.4.1
> 
> 

  reply	other threads:[~2014-11-29 15:22 UTC|newest]

Thread overview: 97+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-22 18:34 [dpdk-dev] [PATCH 00/10] VM Power Management Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 01/10] Channel Manager and Monitor for VM Power Management(Host) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 02/10] VM Power Management CLI(Host) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 03/10] CPU Frequency Power Management(Host) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 04/10] " Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 05/10] VM communication channels for VM Power Management(Guest) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 06/10] Alternate implementation of librte_power " Alan Carew
2014-09-22 19:17   ` Neil Horman
2014-09-23  7:48     ` Carew, Alan
2014-09-22 18:34 ` [dpdk-dev] [PATCH 07/10] Packet format for VM Power Management(Host and Guest) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 08/10] Build system integration for VM Power Management(Guest and Host) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 09/10] VM Power Management Unit Tests(Guest) Alan Carew
2014-09-22 18:34 ` [dpdk-dev] [PATCH 10/10] VM Power Management CLI(Guest) Alan Carew
2014-09-24 17:26 ` [dpdk-dev] [PATCH v2 00/10] VM Power Management Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 01/10] Channel Manager and Monitor for VM Power Management(Host) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 02/10] VM Power Management CLI(Host) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 03/10] CPU Frequency Power Management(Host) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 04/10] VM Power Management application and Makefile Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 05/10] VM Power Management CLI(Guest) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 06/10] VM communication channels for VM Power Management(Guest) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 07/10] librte_power common interface for Guest and Host Alan Carew
2014-09-25 10:10     ` Neil Horman
2014-09-25 17:06       ` Carew, Alan
2014-09-25 17:49         ` Neil Horman
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 08/10] Packet format for VM Power Management(Host and Guest) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 09/10] Build system integration for VM Power Management(Guest and Host) Alan Carew
2014-09-24 17:26   ` [dpdk-dev] [PATCH v2 10/10] VM Power Management Unit Tests Alan Carew
2014-09-25  2:56   ` [dpdk-dev] [PATCH v2 00/10] VM Power Management Liu, Yong
2014-09-29 15:18   ` [dpdk-dev] [PATCH v3 " Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 01/10] Channel Manager and Monitor for VM Power Management(Host) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 02/10] VM Power Management CLI(Host) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 03/10] CPU Frequency Power Management(Host) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 04/10] VM Power Management application and Makefile Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 05/10] VM Power Management CLI(Guest) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 06/10] VM communication channels for VM Power Management(Guest) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 07/10] librte_power common interface for Guest and Host Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 08/10] Packet format for VM Power Management(Host and Guest) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 09/10] Build system integration for VM Power Management(Guest and Host) Alan Carew
2014-09-29 15:18     ` [dpdk-dev] [PATCH v3 10/10] VM Power Management Unit Tests Alan Carew
2014-09-29 17:29     ` [dpdk-dev] [PATCH v3 00/10] VM Power Management Neil Horman
2014-10-12 19:36     ` [dpdk-dev] [PATCH v4 " Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 01/10] Channel Manager and Monitor for VM Power Management(Host) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 02/10] VM Power Management CLI(Host) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 03/10] CPU Frequency Power Management(Host) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 04/10] VM Power Management application and Makefile Alan Carew
2014-10-16 18:28         ` De Lara Guarch, Pablo
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 05/10] VM Power Management CLI(Guest) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 06/10] VM communication channels for VM Power Management(Guest) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 07/10] librte_power common interface for Guest and Host Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 08/10] Packet format for VM Power Management(Host and Guest) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 09/10] Build system integration for VM Power Management(Guest and Host) Alan Carew
2014-10-12 19:36       ` [dpdk-dev] [PATCH v4 10/10] VM Power Management Unit Tests Alan Carew
2014-10-13  6:17       ` [dpdk-dev] [PATCH v4 00/10] VM Power Management Liu, Yong
2014-10-13 20:26       ` Thomas Monjalon
2014-10-14 12:37         ` Carew, Alan
2014-10-14 15:03           ` Thomas Monjalon
2014-10-16 15:21             ` Carew, Alan
2014-10-28 15:21               ` Thomas Monjalon
2014-11-10  9:05                 ` Carew, Alan
2014-11-10 17:54                   ` O'driscoll, Tim
2014-11-21 23:51                     ` Zhu, Heqing
2014-11-22 17:17                     ` Vincent JARDIN
2014-12-09 17:35                       ` Paolo Bonzini
2014-12-11 23:18                         ` Thomas Monjalon
2014-12-12 13:00                           ` Carew, Alan
2014-12-12 14:50                             ` Paolo Bonzini
2014-12-12 16:10                               ` Thomas Monjalon
2014-12-12 16:13                                 ` Paolo Bonzini
2014-11-21 17:42       ` [dpdk-dev] [PATCH v5 00/10] Virtual Machine " Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 01/10] Channel Manager and Monitor for VM Power Management(Host) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 02/10] VM Power Management CLI(Host) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 03/10] CPU Frequency Power Management(Host) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 04/10] VM Power Management application and Makefile Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 05/10] VM Power Management CLI(Guest) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 06/10] VM communication channels for VM Power Management(Guest) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 07/10] librte_power common interface for Guest and Host Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 08/10] Packet format for VM Power Management(Host and Guest) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 09/10] Build system integration for VM Power Management(Guest and Host) Pablo de Lara
2014-11-21 17:42         ` [dpdk-dev] [PATCH v5 10/10] VM Power Management Unit Tests Pablo de Lara
2014-11-25 16:18         ` [dpdk-dev] [PATCH v6 00/10] Virtual Machine Power Management Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 01/10] Channel Manager and Monitor for VM Power Management(Host) Pablo de Lara
2014-11-29 15:21             ` Neil Horman [this message]
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 02/10] VM Power Management CLI(Host) Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 03/10] CPU Frequency Power Management(Host) Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 04/10] VM Power Management application and Makefile Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 05/10] VM Power Management CLI(Guest) Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 06/10] VM communication channels for VM Power Management(Guest) Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 07/10] librte_power common interface for Guest and Host Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 08/10] Packet format for VM Power Management(Host and Guest) Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 09/10] Build system integration for VM Power Management(Guest and Host) Pablo de Lara
2014-11-25 16:18           ` [dpdk-dev] [PATCH v6 10/10] VM Power Management Unit Tests Pablo de Lara
2014-11-26 16:41           ` [dpdk-dev] [PATCH v6 00/10] Virtual Machine Power Management Thomas Monjalon
2014-11-10  9:19     ` [dpdk-dev] [PATCH v2] librte_cmdline: FreeBSD Fix oveflow when size of command result structure is greater than BUFSIZ Alan Carew
2014-12-05 14:16       ` Olivier MATZ
2014-12-05 14:19         ` [dpdk-dev] [PATCH v3] " Olivier Matz
2014-12-05 15:51           ` Bruce Richardson
2014-12-05 15:58             ` Thomas Monjalon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20141129152151.GB14345@localhost.localdomain \
    --to=nhorman@tuxdriver.com \
    --cc=dev@dpdk.org \
    --cc=pablo.de.lara.guarch@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).