From b63264c8342e6a1b6971c79550d2af2024b6a4de Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 14 Aug 2018 18:52:30 +0100 Subject: New upstream version 18.08 Change-Id: I32fdf5e5016556d9c0a6d88ddaf1fc468961790a Signed-off-by: Luca Boccassi --- drivers/net/virtio/virtio_user/vhost_kernel.c | 86 ++++----- drivers/net/virtio/virtio_user/vhost_kernel_tap.c | 14 +- drivers/net/virtio/virtio_user/vhost_kernel_tap.h | 3 +- drivers/net/virtio/virtio_user/vhost_user.c | 76 +++++++- drivers/net/virtio/virtio_user/virtio_user_dev.c | 201 +++++++++++++++++----- drivers/net/virtio/virtio_user/virtio_user_dev.h | 12 +- 6 files changed, 293 insertions(+), 99 deletions(-) (limited to 'drivers/net/virtio/virtio_user') diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c index 8d0a1ab2..b2444096 100644 --- a/drivers/net/virtio/virtio_user/vhost_kernel.c +++ b/drivers/net/virtio/virtio_user/vhost_kernel.c @@ -70,6 +70,32 @@ static uint64_t vhost_req_user_to_kernel[] = { [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE, }; +struct walk_arg { + struct vhost_memory_kernel *vm; + uint32_t region_nr; +}; +static int +add_memory_region(const struct rte_memseg_list *msl __rte_unused, + const struct rte_memseg *ms, size_t len, void *arg) +{ + struct walk_arg *wa = arg; + struct vhost_memory_region *mr; + void *start_addr; + + if (wa->region_nr >= max_regions) + return -1; + + mr = &wa->vm->regions[wa->region_nr++]; + start_addr = ms->addr; + + mr->guest_phys_addr = (uint64_t)(uintptr_t)start_addr; + mr->userspace_addr = (uint64_t)(uintptr_t)start_addr; + mr->memory_size = len; + mr->mmap_offset = 0; + + return 0; +} + /* By default, vhost kernel module allows 64 regions, but DPDK allows * 256 segments. As a relief, below function merges those virtually * adjacent memsegs into one region. @@ -77,63 +103,24 @@ static uint64_t vhost_req_user_to_kernel[] = { static struct vhost_memory_kernel * prepare_vhost_memory_kernel(void) { - uint32_t i, j, k = 0; - struct rte_memseg *seg; - struct vhost_memory_region *mr; struct vhost_memory_kernel *vm; + struct walk_arg wa; vm = malloc(sizeof(struct vhost_memory_kernel) + - max_regions * - sizeof(struct vhost_memory_region)); + max_regions * + sizeof(struct vhost_memory_region)); if (!vm) return NULL; - for (i = 0; i < RTE_MAX_MEMSEG; ++i) { - seg = &rte_eal_get_configuration()->mem_config->memseg[i]; - if (!seg->addr) - break; - - int new_region = 1; - - for (j = 0; j < k; ++j) { - mr = &vm->regions[j]; - - if (mr->userspace_addr + mr->memory_size == - (uint64_t)(uintptr_t)seg->addr) { - mr->memory_size += seg->len; - new_region = 0; - break; - } - - if ((uint64_t)(uintptr_t)seg->addr + seg->len == - mr->userspace_addr) { - mr->guest_phys_addr = - (uint64_t)(uintptr_t)seg->addr; - mr->userspace_addr = - (uint64_t)(uintptr_t)seg->addr; - mr->memory_size += seg->len; - new_region = 0; - break; - } - } - - if (new_region == 0) - continue; - - mr = &vm->regions[k++]; - /* use vaddr here! */ - mr->guest_phys_addr = (uint64_t)(uintptr_t)seg->addr; - mr->userspace_addr = (uint64_t)(uintptr_t)seg->addr; - mr->memory_size = seg->len; - mr->mmap_offset = 0; + wa.region_nr = 0; + wa.vm = vm; - if (k >= max_regions) { - free(vm); - return NULL; - } + if (rte_memseg_contig_walk(add_memory_region, &wa) < 0) { + free(vm); + return NULL; } - vm->nregions = k; + vm->nregions = wa.region_nr; vm->padding = 0; return vm; } @@ -351,7 +338,8 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev, else hdr_size = sizeof(struct virtio_net_hdr); - tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size, req_mq); + tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size, req_mq, + (char *)dev->mac_addr); if (tapfd < 0) { PMD_DRV_LOG(ERR, "fail to open tap for vhost kernel"); return -1; diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c index 1a47a348..9ea7ade7 100644 --- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c +++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c @@ -7,15 +7,19 @@ #include #include #include +#include #include #include #include +#include + #include "vhost_kernel_tap.h" #include "../virtio_logs.h" int -vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq) +vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq, + const char *mac) { unsigned int tap_features; int sndbuf = INT_MAX; @@ -94,6 +98,14 @@ vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq) PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s", strerror(errno)); + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(ifr.ifr_hwaddr.sa_data, mac, ETHER_ADDR_LEN); + if (ioctl(tapfd, SIOCSIFHWADDR, (void *)&ifr) == -1) { + PMD_DRV_LOG(ERR, "SIOCSIFHWADDR failed: %s", strerror(errno)); + goto error; + } + if (!(*p_ifname)) *p_ifname = strdup(ifr.ifr_name); diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h index 7d52e6b7..01a026f5 100644 --- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h +++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h @@ -35,4 +35,5 @@ /* Constants */ #define PATH_NET_TUN "/dev/net/tun" -int vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq); +int vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq, + const char *mac); diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c index 91c6449b..ef6e43df 100644 --- a/drivers/net/virtio/virtio_user/vhost_user.c +++ b/drivers/net/virtio/virtio_user/vhost_user.c @@ -138,12 +138,13 @@ struct hugepage_file_info { static int get_hugepage_file_info(struct hugepage_file_info huges[], int max) { - int idx; + int idx, k, exist; FILE *f; char buf[BUFSIZ], *tmp, *tail; char *str_underline, *str_start; int huge_index; uint64_t v_start, v_end; + struct stat stats; f = fopen("/proc/self/maps", "r"); if (!f) { @@ -183,16 +184,39 @@ get_hugepage_file_info(struct hugepage_file_info huges[], int max) if (sscanf(str_start, "map_%d", &huge_index) != 1) continue; + /* skip duplicated file which is mapped to different regions */ + for (k = 0, exist = -1; k < idx; ++k) { + if (!strcmp(huges[k].path, tmp)) { + exist = k; + break; + } + } + if (exist >= 0) + continue; + if (idx >= max) { PMD_DRV_LOG(ERR, "Exceed maximum of %d", max); goto error; } + huges[idx].addr = v_start; - huges[idx].size = v_end - v_start; + huges[idx].size = v_end - v_start; /* To be corrected later */ snprintf(huges[idx].path, PATH_MAX, "%s", tmp); idx++; } + /* correct the size for files who have many regions */ + for (k = 0; k < idx; ++k) { + if (stat(huges[k].path, &stats) < 0) { + PMD_DRV_LOG(ERR, "Failed to stat %s, %s\n", + huges[k].path, strerror(errno)); + continue; + } + huges[k].size = stats.st_size; + PMD_DRV_LOG(INFO, "file %s, size %zx\n", + huges[k].path, huges[k].size); + } + fclose(f); return idx; @@ -263,6 +287,9 @@ vhost_user_sock(struct virtio_user_dev *dev, PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]); + if (dev->is_server && vhostfd < 0) + return -1; + msg.request = req; msg.flags = VHOST_USER_VERSION; msg.size = 0; @@ -378,6 +405,30 @@ vhost_user_sock(struct virtio_user_dev *dev, return 0; } +#define MAX_VIRTIO_USER_BACKLOG 1 +static int +virtio_user_start_server(struct virtio_user_dev *dev, struct sockaddr_un *un) +{ + int ret; + int flag; + int fd = dev->listenfd; + + ret = bind(fd, (struct sockaddr *)un, sizeof(*un)); + if (ret < 0) { + PMD_DRV_LOG(ERR, "failed to bind to %s: %s; remove it and try again\n", + dev->path, strerror(errno)); + return -1; + } + ret = listen(fd, MAX_VIRTIO_USER_BACKLOG); + if (ret < 0) + return -1; + + flag = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flag | O_NONBLOCK); + + return 0; +} + /** * Set up environment to talk with a vhost user backend. * @@ -405,13 +456,24 @@ vhost_user_setup(struct virtio_user_dev *dev) memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path); - if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) { - PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno)); - close(fd); - return -1; + + if (dev->is_server) { + dev->listenfd = fd; + if (virtio_user_start_server(dev, &un) < 0) { + PMD_DRV_LOG(ERR, "virtio-user startup fails in server mode"); + close(fd); + return -1; + } + dev->vhostfd = -1; + } else { + if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) { + PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno)); + close(fd); + return -1; + } + dev->vhostfd = fd; } - dev->vhostfd = fd; return 0; } diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c index f90fee9e..7df600b0 100644 --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c @@ -17,6 +17,8 @@ #include "virtio_user_dev.h" #include "../virtio_ethdev.h" +#define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb" + static int virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel) { @@ -93,12 +95,28 @@ virtio_user_queue_setup(struct virtio_user_dev *dev, return 0; } +int +is_vhost_user_by_type(const char *path) +{ + struct stat sb; + + if (stat(path, &sb) == -1) + return 0; + + return S_ISSOCK(sb.st_mode); +} + int virtio_user_start_device(struct virtio_user_dev *dev) { uint64_t features; int ret; + pthread_mutex_lock(&dev->mutex); + + if (is_vhost_user_by_type(dev->path) && dev->vhostfd < 0) + goto error; + /* Do not check return as already done in init, or reset in stop */ dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL); @@ -132,8 +150,12 @@ virtio_user_start_device(struct virtio_user_dev *dev) */ dev->ops->enable_qp(dev, 0, 1); + dev->started = true; + pthread_mutex_unlock(&dev->mutex); + return 0; error: + pthread_mutex_unlock(&dev->mutex); /* TODO: free resource here or caller to check */ return -1; } @@ -142,13 +164,17 @@ int virtio_user_stop_device(struct virtio_user_dev *dev) { uint32_t i; + pthread_mutex_lock(&dev->mutex); for (i = 0; i < dev->max_queue_pairs; ++i) dev->ops->enable_qp(dev, i, 0); if (dev->ops->send_request(dev, VHOST_USER_RESET_OWNER, NULL) < 0) { PMD_DRV_LOG(INFO, "Failed to reset the device\n"); + pthread_mutex_unlock(&dev->mutex); return -1; } + dev->started = false; + pthread_mutex_unlock(&dev->mutex); return 0; } @@ -174,17 +200,6 @@ parse_mac(struct virtio_user_dev *dev, const char *mac) } } -int -is_vhost_user_by_type(const char *path) -{ - struct stat sb; - - if (stat(path, &sb) == -1) - return 0; - - return S_ISSOCK(sb.st_mode); -} - static int virtio_user_dev_init_notify(struct virtio_user_dev *dev) { @@ -254,10 +269,41 @@ virtio_user_fill_intr_handle(struct virtio_user_dev *dev) eth_dev->intr_handle->fd = -1; if (dev->vhostfd >= 0) eth_dev->intr_handle->fd = dev->vhostfd; + else if (dev->is_server) + eth_dev->intr_handle->fd = dev->listenfd; return 0; } +static void +virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused, + const void *addr __rte_unused, + size_t len __rte_unused, + void *arg) +{ + struct virtio_user_dev *dev = arg; + uint16_t i; + + pthread_mutex_lock(&dev->mutex); + + if (dev->started == false) + goto exit; + + /* Step 1: pause the active queues */ + for (i = 0; i < dev->queue_pairs; i++) + dev->ops->enable_qp(dev, i, 0); + + /* Step 2: update memory regions */ + dev->ops->send_request(dev, VHOST_USER_SET_MEM_TABLE, NULL); + + /* Step 3: resume the active queues */ + for (i = 0; i < dev->queue_pairs; i++) + dev->ops->enable_qp(dev, i, 1); + +exit: + pthread_mutex_unlock(&dev->mutex); +} + static int virtio_user_dev_setup(struct virtio_user_dev *dev) { @@ -267,21 +313,32 @@ virtio_user_dev_setup(struct virtio_user_dev *dev) dev->vhostfds = NULL; dev->tapfds = NULL; - if (is_vhost_user_by_type(dev->path)) { - dev->ops = &ops_user; - } else { - dev->ops = &ops_kernel; - - dev->vhostfds = malloc(dev->max_queue_pairs * sizeof(int)); - dev->tapfds = malloc(dev->max_queue_pairs * sizeof(int)); - if (!dev->vhostfds || !dev->tapfds) { - PMD_INIT_LOG(ERR, "Failed to malloc"); + if (dev->is_server) { + if (access(dev->path, F_OK) == 0 && + !is_vhost_user_by_type(dev->path)) { + PMD_DRV_LOG(ERR, "Server mode doesn't support vhost-kernel!"); return -1; } - - for (q = 0; q < dev->max_queue_pairs; ++q) { - dev->vhostfds[q] = -1; - dev->tapfds[q] = -1; + dev->ops = &ops_user; + } else { + if (is_vhost_user_by_type(dev->path)) { + dev->ops = &ops_user; + } else { + dev->ops = &ops_kernel; + + dev->vhostfds = malloc(dev->max_queue_pairs * + sizeof(int)); + dev->tapfds = malloc(dev->max_queue_pairs * + sizeof(int)); + if (!dev->vhostfds || !dev->tapfds) { + PMD_INIT_LOG(ERR, "Failed to malloc"); + return -1; + } + + for (q = 0; q < dev->max_queue_pairs; ++q) { + dev->vhostfds[q] = -1; + dev->tapfds[q] = -1; + } } } @@ -314,17 +371,22 @@ virtio_user_dev_setup(struct virtio_user_dev *dev) 1ULL << VIRTIO_NET_F_GUEST_CSUM | \ 1ULL << VIRTIO_NET_F_GUEST_TSO4 | \ 1ULL << VIRTIO_NET_F_GUEST_TSO6 | \ + 1ULL << VIRTIO_F_IN_ORDER | \ 1ULL << VIRTIO_F_VERSION_1) int virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, - int cq, int queue_size, const char *mac, char **ifname) + int cq, int queue_size, const char *mac, char **ifname, + int mrg_rxbuf, int in_order) { + pthread_mutex_init(&dev->mutex, NULL); snprintf(dev->path, PATH_MAX, "%s", path); + dev->started = 0; dev->max_queue_pairs = queues; dev->queue_pairs = 1; /* mq disabled by default */ dev->queue_size = queue_size; dev->mac_specified = 0; + dev->unsupported_features = 0; parse_mac(dev, mac); if (*ifname) { @@ -337,18 +399,45 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, return -1; } - if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) { - PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno)); - return -1; + if (!dev->is_server) { + if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, + NULL) < 0) { + PMD_INIT_LOG(ERR, "set_owner fails: %s", + strerror(errno)); + return -1; + } + + if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES, + &dev->device_features) < 0) { + PMD_INIT_LOG(ERR, "get_features failed: %s", + strerror(errno)); + return -1; + } + } else { + /* We just pretend vhost-user can support all these features. + * Note that this could be problematic that if some feature is + * negotiated but not supported by the vhost-user which comes + * later. + */ + dev->device_features = VIRTIO_USER_SUPPORTED_FEATURES; } - if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES, - &dev->device_features) < 0) { - PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno)); - return -1; + if (!mrg_rxbuf) { + dev->device_features &= ~(1ull << VIRTIO_NET_F_MRG_RXBUF); + dev->unsupported_features |= (1ull << VIRTIO_NET_F_MRG_RXBUF); + } + + if (!in_order) { + dev->device_features &= ~(1ull << VIRTIO_F_IN_ORDER); + dev->unsupported_features |= (1ull << VIRTIO_F_IN_ORDER); } - if (dev->mac_specified) + + if (dev->mac_specified) { dev->device_features |= (1ull << VIRTIO_NET_F_MAC); + } else { + dev->device_features &= ~(1ull << VIRTIO_NET_F_MAC); + dev->unsupported_features |= (1ull << VIRTIO_NET_F_MAC); + } if (cq) { /* device does not really need to know anything about CQ, @@ -363,6 +452,14 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, dev->device_features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE); dev->device_features &= ~(1ull << VIRTIO_NET_F_MQ); dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR); + dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VQ); + dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_RX); + dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VLAN); + dev->unsupported_features |= + (1ull << VIRTIO_NET_F_GUEST_ANNOUNCE); + dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ); + dev->unsupported_features |= + (1ull << VIRTIO_NET_F_CTRL_MAC_ADDR); } /* The backend will not report this feature, we add it explicitly */ @@ -370,6 +467,16 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, dev->device_features |= (1ull << VIRTIO_NET_F_STATUS); dev->device_features &= VIRTIO_USER_SUPPORTED_FEATURES; + dev->unsupported_features |= ~VIRTIO_USER_SUPPORTED_FEATURES; + + if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME, + virtio_user_mem_event_cb, dev)) { + if (rte_errno != ENOTSUP) { + PMD_INIT_LOG(ERR, "Failed to register mem event" + " callback\n"); + return -1; + } + } return 0; } @@ -381,6 +488,8 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev) virtio_user_stop_device(dev); + rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev); + for (i = 0; i < dev->max_queue_pairs * 2; ++i) { close(dev->callfds[i]); close(dev->kickfds[i]); @@ -388,6 +497,11 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev) close(dev->vhostfd); + if (dev->is_server && dev->listenfd >= 0) { + close(dev->listenfd); + dev->listenfd = -1; + } + if (dev->vhostfds) { for (i = 0; i < dev->max_queue_pairs; ++i) close(dev->vhostfds[i]); @@ -396,9 +510,12 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev) } free(dev->ifname); + + if (dev->is_server) + unlink(dev->path); } -static uint8_t +uint8_t virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs) { uint16_t i; @@ -410,11 +527,17 @@ virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs) return -1; } - for (i = 0; i < q_pairs; ++i) - ret |= dev->ops->enable_qp(dev, i, 1); - for (i = q_pairs; i < dev->max_queue_pairs; ++i) - ret |= dev->ops->enable_qp(dev, i, 0); - + /* Server mode can't enable queue pairs if vhostfd is invalid, + * always return 0 in this case. + */ + if (dev->vhostfd >= 0) { + for (i = 0; i < q_pairs; ++i) + ret |= dev->ops->enable_qp(dev, i, 1); + for (i = q_pairs; i < dev->max_queue_pairs; ++i) + ret |= dev->ops->enable_qp(dev, i, 0); + } else if (!dev->is_server) { + ret = ~0; + } dev->queue_pairs = q_pairs; return ret; diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h index 64467b4f..d6e0e137 100644 --- a/drivers/net/virtio/virtio_user/virtio_user_dev.h +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h @@ -6,6 +6,7 @@ #define _VIRTIO_USER_DEV_H #include +#include #include "../virtio_pci.h" #include "../virtio_ring.h" #include "vhost.h" @@ -13,6 +14,8 @@ struct virtio_user_dev { /* for vhost_user backend */ int vhostfd; + int listenfd; /* listening fd */ + bool is_server; /* server or client mode */ /* for vhost_kernel backend */ char *ifname; @@ -30,19 +33,24 @@ struct virtio_user_dev { * and will be sync with device */ uint64_t device_features; /* supported features by device */ + uint64_t unsupported_features; /* unsupported features mask */ uint8_t status; - uint8_t port_id; + uint16_t port_id; uint8_t mac_addr[ETHER_ADDR_LEN]; char path[PATH_MAX]; struct vring vrings[VIRTIO_MAX_VIRTQUEUES]; struct virtio_user_backend_ops *ops; + pthread_mutex_t mutex; + bool started; }; int is_vhost_user_by_type(const char *path); int virtio_user_start_device(struct virtio_user_dev *dev); int virtio_user_stop_device(struct virtio_user_dev *dev); int virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, - int cq, int queue_size, const char *mac, char **ifname); + int cq, int queue_size, const char *mac, char **ifname, + int mrg_rxbuf, int in_order); void virtio_user_dev_uninit(struct virtio_user_dev *dev); void virtio_user_handle_cq(struct virtio_user_dev *dev, uint16_t queue_idx); +uint8_t virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs); #endif -- cgit 1.2.3-korg