831dba47bd
This patch adds support for an additional bus type Virtual Machine BUS (VMBUS) on Microsoft Hyper-V in Windows 10, Windows Server 2016 and Azure. Most of this code was extracted from FreeBSD and some of this is from earlier code donated by Brocade. Only Linux is supported at present, but the code is split to allow future FreeBSD and Windows support. The bus support relies on the uio_hv_generic driver from Linux kernel 4.16. Multiple queue support requires additional sysfs interfaces which is in kernel 5.0 (a.k.a 4.17). Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
233 lines
5.2 KiB
C
233 lines
5.2 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (c) 2018, Microsoft Corporation.
|
|
* All Rights Reserved.
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <rte_eal.h>
|
|
#include <rte_tailq.h>
|
|
#include <rte_log.h>
|
|
#include <rte_malloc.h>
|
|
#include <rte_bus.h>
|
|
#include <rte_bus_vmbus.h>
|
|
|
|
#include "private.h"
|
|
|
|
static struct rte_tailq_elem vmbus_tailq = {
|
|
.name = "VMBUS_RESOURCE_LIST",
|
|
};
|
|
EAL_REGISTER_TAILQ(vmbus_tailq)
|
|
|
|
static int
|
|
vmbus_uio_map_secondary(struct rte_vmbus_device *dev)
|
|
{
|
|
int fd, i;
|
|
struct mapped_vmbus_resource *uio_res;
|
|
struct mapped_vmbus_res_list *uio_res_list
|
|
= RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
|
|
|
|
TAILQ_FOREACH(uio_res, uio_res_list, next) {
|
|
|
|
/* skip this element if it doesn't match our UUID */
|
|
if (rte_uuid_compare(uio_res->id, dev->device_id) != 0)
|
|
continue;
|
|
|
|
/* open /dev/uioX */
|
|
fd = open(uio_res->path, O_RDWR);
|
|
if (fd < 0) {
|
|
VMBUS_LOG(ERR, "Cannot open %s: %s",
|
|
uio_res->path, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i != uio_res->nb_maps; i++) {
|
|
void *mapaddr;
|
|
|
|
mapaddr = vmbus_map_resource(uio_res->maps[i].addr,
|
|
fd, 0,
|
|
uio_res->maps[i].size, 0);
|
|
|
|
if (mapaddr == uio_res->maps[i].addr)
|
|
continue;
|
|
|
|
VMBUS_LOG(ERR,
|
|
"Cannot mmap device resource file %s to address: %p",
|
|
uio_res->path, uio_res->maps[i].addr);
|
|
|
|
if (mapaddr != MAP_FAILED)
|
|
/* unmap addr wrongly mapped */
|
|
vmbus_unmap_resource(mapaddr,
|
|
(size_t)uio_res->maps[i].size);
|
|
|
|
/* unmap addrs correctly mapped */
|
|
while (--i >= 0)
|
|
vmbus_unmap_resource(uio_res->maps[i].addr,
|
|
(size_t)uio_res->maps[i].size);
|
|
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
/* fd is not needed in slave process, close it */
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
VMBUS_LOG(ERR, "Cannot find resource for device");
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
vmbus_uio_map_primary(struct rte_vmbus_device *dev)
|
|
{
|
|
int i, ret;
|
|
struct mapped_vmbus_resource *uio_res = NULL;
|
|
struct mapped_vmbus_res_list *uio_res_list =
|
|
RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
|
|
|
|
/* allocate uio resource */
|
|
ret = vmbus_uio_alloc_resource(dev, &uio_res);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Map the resources */
|
|
for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
|
|
/* skip empty BAR */
|
|
if (dev->resource[i].len == 0)
|
|
continue;
|
|
|
|
ret = vmbus_uio_map_resource_by_index(dev, i, uio_res, 0);
|
|
if (ret)
|
|
goto error;
|
|
}
|
|
|
|
uio_res->nb_maps = i;
|
|
|
|
TAILQ_INSERT_TAIL(uio_res_list, uio_res, next);
|
|
|
|
return 0;
|
|
error:
|
|
while (--i >= 0) {
|
|
vmbus_unmap_resource(uio_res->maps[i].addr,
|
|
(size_t)uio_res->maps[i].size);
|
|
}
|
|
vmbus_uio_free_resource(dev, uio_res);
|
|
return -1;
|
|
}
|
|
|
|
|
|
struct mapped_vmbus_resource *
|
|
vmbus_uio_find_resource(const struct rte_vmbus_device *dev)
|
|
{
|
|
struct mapped_vmbus_resource *uio_res;
|
|
struct mapped_vmbus_res_list *uio_res_list =
|
|
RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
|
|
|
|
if (dev == NULL)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(uio_res, uio_res_list, next) {
|
|
/* skip this element if it doesn't match our VMBUS address */
|
|
if (rte_uuid_compare(uio_res->id, dev->device_id) == 0)
|
|
return uio_res;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* map the VMBUS resource of a VMBUS device in virtual memory */
|
|
int
|
|
vmbus_uio_map_resource(struct rte_vmbus_device *dev)
|
|
{
|
|
struct mapped_vmbus_resource *uio_res;
|
|
int ret;
|
|
|
|
/* TODO: handle rescind */
|
|
dev->intr_handle.fd = -1;
|
|
dev->intr_handle.uio_cfg_fd = -1;
|
|
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
|
|
|
|
/* secondary processes - use already recorded details */
|
|
if (rte_eal_process_type() != RTE_PROC_PRIMARY)
|
|
ret = vmbus_uio_map_secondary(dev);
|
|
else
|
|
ret = vmbus_uio_map_primary(dev);
|
|
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
uio_res = vmbus_uio_find_resource(dev);
|
|
if (!uio_res) {
|
|
VMBUS_LOG(ERR, "can not find resources!");
|
|
return -EIO;
|
|
}
|
|
|
|
if (uio_res->nb_maps <= HV_MON_PAGE_MAP) {
|
|
VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
|
|
uio_res->nb_maps);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev->int_page = (uint32_t *)((char *)uio_res->maps[HV_INT_PAGE_MAP].addr
|
|
+ (PAGE_SIZE >> 1));
|
|
dev->monitor_page = uio_res->maps[HV_MON_PAGE_MAP].addr;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
vmbus_uio_unmap(struct mapped_vmbus_resource *uio_res)
|
|
{
|
|
int i;
|
|
|
|
if (uio_res == NULL)
|
|
return;
|
|
|
|
for (i = 0; i != uio_res->nb_maps; i++) {
|
|
vmbus_unmap_resource(uio_res->maps[i].addr,
|
|
(size_t)uio_res->maps[i].size);
|
|
}
|
|
}
|
|
|
|
/* unmap the VMBUS resource of a VMBUS device in virtual memory */
|
|
void
|
|
vmbus_uio_unmap_resource(struct rte_vmbus_device *dev)
|
|
{
|
|
struct mapped_vmbus_resource *uio_res;
|
|
struct mapped_vmbus_res_list *uio_res_list =
|
|
RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
|
|
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
/* find an entry for the device */
|
|
uio_res = vmbus_uio_find_resource(dev);
|
|
if (uio_res == NULL)
|
|
return;
|
|
|
|
/* secondary processes - just free maps */
|
|
if (rte_eal_process_type() != RTE_PROC_PRIMARY)
|
|
return vmbus_uio_unmap(uio_res);
|
|
|
|
TAILQ_REMOVE(uio_res_list, uio_res, next);
|
|
|
|
/* unmap all resources */
|
|
vmbus_uio_unmap(uio_res);
|
|
|
|
/* free uio resource */
|
|
rte_free(uio_res);
|
|
|
|
/* close fd if in primary process */
|
|
close(dev->intr_handle.fd);
|
|
if (dev->intr_handle.uio_cfg_fd >= 0) {
|
|
close(dev->intr_handle.uio_cfg_fd);
|
|
dev->intr_handle.uio_cfg_fd = -1;
|
|
}
|
|
|
|
dev->intr_handle.fd = -1;
|
|
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
|
|
}
|