numam-dpdk/drivers/bus/vmbus/vmbus_common_uio.c
Long Li 70cdd92e04 bus/vmbus: fix ring buffer mapping in secondary process
The driver code had wrong assumption that all the addresses to ring buffers
in the secondary process are the same as those in the primary process. This
is not always correct as the channels could be mapped to different
addresses in the secondary process.

Fix this by keeping track of all the mapped addresses from the primary
process in the shared uio_res, and have second process map to the same
addresses.

Fixes: 831dba47bd ("bus/vmbus: add Hyper-V virtual bus support")
Cc: stable@dpdk.org

Reported-by: Jonathan Erb <jonathan.erb@banduracyber.com>
Signed-off-by: Long Li <longli@microsoft.com>
Acked-by: Stephen Hemminger <sthemmin@microsoft.com>
2021-10-13 13:55:09 +02:00

265 lines
5.9 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)
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) {
if (rte_uuid_compare(uio_res->id, dev->device_id) == 0)
return uio_res;
}
return NULL;
}
static int
vmbus_uio_map_secondary(struct rte_vmbus_device *dev)
{
struct mapped_vmbus_resource *uio_res;
struct vmbus_channel *chan;
int fd, i;
uio_res = vmbus_uio_find_resource(dev);
if (!uio_res) {
VMBUS_LOG(ERR, "Cannot find resource for device");
return -1;
}
/* 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;
off_t offset = i * rte_mem_page_size();
mapaddr = vmbus_map_resource(uio_res->maps[i].addr,
fd, offset,
uio_res->maps[i].size, 0);
if (mapaddr == uio_res->maps[i].addr) {
dev->resource[i].addr = mapaddr;
continue; /* successful map */
}
if (mapaddr == MAP_FAILED)
VMBUS_LOG(ERR,
"mmap resource %d in secondary failed", i);
else {
VMBUS_LOG(ERR,
"mmap resource %d address mismatch", i);
vmbus_unmap_resource(mapaddr, uio_res->maps[i].size);
}
close(fd);
return -1;
}
/* fd is not needed in secondary process, close it */
close(fd);
/* Create and map primary channel */
if (vmbus_chan_create(dev, dev->relid, 0,
dev->monitor_id, &dev->primary)) {
VMBUS_LOG(ERR, "cannot create primary channel");
goto failed_primary;
}
/* Create and map sub channels */
for (i = 0; i < uio_res->nb_subchannels; i++) {
if (rte_vmbus_subchan_open(dev->primary, &chan)) {
VMBUS_LOG(ERR,
"failed to create subchannel at index %d", i);
goto failed_secondary;
}
}
return 0;
failed_secondary:
while (!STAILQ_EMPTY(&dev->primary->subchannel_list)) {
chan = STAILQ_FIRST(&dev->primary->subchannel_list);
vmbus_unmap_resource(chan->txbr.vbr, chan->txbr.dsize * 2);
rte_vmbus_chan_close(chan);
}
rte_vmbus_chan_close(dev->primary);
failed_primary:
for (i = 0; i != uio_res->nb_maps; i++) {
vmbus_unmap_resource(
uio_res->maps[i].addr, uio_res->maps[i].size);
}
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++) {
/* stop at empty BAR */
if (dev->resource[i].len == 0)
break;
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;
}
/* 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
+ (rte_mem_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_subchannels; i++) {
vmbus_unmap_resource(uio_res->subchannel_maps[i].addr,
uio_res->subchannel_maps[i].size);
}
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) {
vmbus_uio_unmap(uio_res);
rte_free(dev->primary);
return;
}
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;
}