70cdd92e04
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>
265 lines
5.9 KiB
C
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;
|
|
}
|