summaryrefslogtreecommitdiffstats
path: root/vdpa
diff options
context:
space:
mode:
Diffstat (limited to 'vdpa')
-rw-r--r--vdpa/.gitignore1
-rw-r--r--vdpa/Makefile19
-rw-r--r--vdpa/include/uapi/linux/vdpa.h65
-rw-r--r--vdpa/include/uapi/linux/virtio_ids.h84
-rw-r--r--vdpa/include/uapi/linux/virtio_ring.h248
-rw-r--r--vdpa/vdpa.c1139
6 files changed, 1556 insertions, 0 deletions
diff --git a/vdpa/.gitignore b/vdpa/.gitignore
new file mode 100644
index 0000000..7ef2878
--- /dev/null
+++ b/vdpa/.gitignore
@@ -0,0 +1 @@
+vdpa
diff --git a/vdpa/Makefile b/vdpa/Makefile
new file mode 100644
index 0000000..86f7221
--- /dev/null
+++ b/vdpa/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../config.mk
+
+CFLAGS += -I./include/uapi/
+VDPAOBJ = vdpa.o
+TARGETS += vdpa
+
+all: $(TARGETS) $(LIBS)
+
+vdpa: $(VDPAOBJ)
+ $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
+
+install: all
+ for i in $(TARGETS); \
+ do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \
+ done
+
+clean:
+ rm -f $(VDPAOBJ) $(TARGETS)
diff --git a/vdpa/include/uapi/linux/vdpa.h b/vdpa/include/uapi/linux/vdpa.h
new file mode 100644
index 0000000..0561852
--- /dev/null
+++ b/vdpa/include/uapi/linux/vdpa.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * vdpa device management interface
+ * Copyright (c) 2020 Mellanox Technologies Ltd. All rights reserved.
+ */
+
+#ifndef _LINUX_VDPA_H_
+#define _LINUX_VDPA_H_
+
+#define VDPA_GENL_NAME "vdpa"
+#define VDPA_GENL_VERSION 0x1
+
+enum vdpa_command {
+ VDPA_CMD_UNSPEC,
+ VDPA_CMD_MGMTDEV_NEW,
+ VDPA_CMD_MGMTDEV_GET, /* can dump */
+ VDPA_CMD_DEV_NEW,
+ VDPA_CMD_DEV_DEL,
+ VDPA_CMD_DEV_GET, /* can dump */
+ VDPA_CMD_DEV_CONFIG_GET, /* can dump */
+ VDPA_CMD_DEV_VSTATS_GET,
+};
+
+enum vdpa_attr {
+ VDPA_ATTR_UNSPEC,
+
+ /* Pad attribute for 64b alignment */
+ VDPA_ATTR_PAD = VDPA_ATTR_UNSPEC,
+
+ /* bus name (optional) + dev name together make the parent device handle */
+ VDPA_ATTR_MGMTDEV_BUS_NAME, /* string */
+ VDPA_ATTR_MGMTDEV_DEV_NAME, /* string */
+ VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES, /* u64 */
+
+ VDPA_ATTR_DEV_NAME, /* string */
+ VDPA_ATTR_DEV_ID, /* u32 */
+ VDPA_ATTR_DEV_VENDOR_ID, /* u32 */
+ VDPA_ATTR_DEV_MAX_VQS, /* u32 */
+ VDPA_ATTR_DEV_MAX_VQ_SIZE, /* u16 */
+ VDPA_ATTR_DEV_MIN_VQ_SIZE, /* u16 */
+
+ VDPA_ATTR_DEV_NET_CFG_MACADDR, /* binary */
+ VDPA_ATTR_DEV_NET_STATUS, /* u8 */
+ VDPA_ATTR_DEV_NET_CFG_MAX_VQP, /* u16 */
+ VDPA_ATTR_DEV_NET_CFG_MTU, /* u16 */
+
+ VDPA_ATTR_DEV_NEGOTIATED_FEATURES, /* u64 */
+ VDPA_ATTR_DEV_MGMTDEV_MAX_VQS, /* u32 */
+ /* virtio features that are supported by the vDPA management device */
+ VDPA_ATTR_DEV_SUPPORTED_FEATURES, /* u64 */
+
+ VDPA_ATTR_DEV_QUEUE_INDEX, /* u32 */
+ VDPA_ATTR_DEV_VENDOR_ATTR_NAME, /* string */
+ VDPA_ATTR_DEV_VENDOR_ATTR_VALUE, /* u64 */
+
+ VDPA_ATTR_DEV_FEATURES, /* u64 */
+
+ /* virtio features that are supported by the vDPA device */
+ VDPA_ATTR_VDPA_DEV_SUPPORTED_FEATURES, /* u64 */
+
+ /* new attributes must be added above here */
+ VDPA_ATTR_MAX,
+};
+
+#endif
diff --git a/vdpa/include/uapi/linux/virtio_ids.h b/vdpa/include/uapi/linux/virtio_ids.h
new file mode 100644
index 0000000..7aa2eb7
--- /dev/null
+++ b/vdpa/include/uapi/linux/virtio_ids.h
@@ -0,0 +1,84 @@
+#ifndef _LINUX_VIRTIO_IDS_H
+#define _LINUX_VIRTIO_IDS_H
+/*
+ * Virtio IDs
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of IBM 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 IBM 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. */
+
+#define VIRTIO_ID_NET 1 /* virtio net */
+#define VIRTIO_ID_BLOCK 2 /* virtio block */
+#define VIRTIO_ID_CONSOLE 3 /* virtio console */
+#define VIRTIO_ID_RNG 4 /* virtio rng */
+#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
+#define VIRTIO_ID_IOMEM 6 /* virtio ioMemory */
+#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */
+#define VIRTIO_ID_SCSI 8 /* virtio scsi */
+#define VIRTIO_ID_9P 9 /* 9p virtio console */
+#define VIRTIO_ID_MAC80211_WLAN 10 /* virtio WLAN MAC */
+#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
+#define VIRTIO_ID_CAIF 12 /* Virtio caif */
+#define VIRTIO_ID_MEMORY_BALLOON 13 /* virtio memory balloon */
+#define VIRTIO_ID_GPU 16 /* virtio GPU */
+#define VIRTIO_ID_CLOCK 17 /* virtio clock/timer */
+#define VIRTIO_ID_INPUT 18 /* virtio input */
+#define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
+#define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
+#define VIRTIO_ID_SIGNAL_DIST 21 /* virtio signal distribution device */
+#define VIRTIO_ID_PSTORE 22 /* virtio pstore device */
+#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
+#define VIRTIO_ID_MEM 24 /* virtio mem */
+#define VIRTIO_ID_SOUND 25 /* virtio sound */
+#define VIRTIO_ID_FS 26 /* virtio filesystem */
+#define VIRTIO_ID_PMEM 27 /* virtio pmem */
+#define VIRTIO_ID_RPMB 28 /* virtio rpmb */
+#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
+#define VIRTIO_ID_VIDEO_ENCODER 30 /* virtio video encoder */
+#define VIRTIO_ID_VIDEO_DECODER 31 /* virtio video decoder */
+#define VIRTIO_ID_SCMI 32 /* virtio SCMI */
+#define VIRTIO_ID_NITRO_SEC_MOD 33 /* virtio nitro secure module*/
+#define VIRTIO_ID_I2C_ADAPTER 34 /* virtio i2c adapter */
+#define VIRTIO_ID_WATCHDOG 35 /* virtio watchdog */
+#define VIRTIO_ID_CAN 36 /* virtio can */
+#define VIRTIO_ID_DMABUF 37 /* virtio dmabuf */
+#define VIRTIO_ID_PARAM_SERV 38 /* virtio parameter server */
+#define VIRTIO_ID_AUDIO_POLICY 39 /* virtio audio policy */
+#define VIRTIO_ID_BT 40 /* virtio bluetooth */
+#define VIRTIO_ID_GPIO 41 /* virtio gpio */
+
+/*
+ * Virtio Transitional IDs
+ */
+
+#define VIRTIO_TRANS_ID_NET 0x1000 /* transitional virtio net */
+#define VIRTIO_TRANS_ID_BLOCK 0x1001 /* transitional virtio block */
+#define VIRTIO_TRANS_ID_BALLOON 0x1002 /* transitional virtio balloon */
+#define VIRTIO_TRANS_ID_CONSOLE 0x1003 /* transitional virtio console */
+#define VIRTIO_TRANS_ID_SCSI 0x1004 /* transitional virtio SCSI */
+#define VIRTIO_TRANS_ID_RNG 0x1005 /* transitional virtio rng */
+#define VIRTIO_TRANS_ID_9P 0x1009 /* transitional virtio 9p console */
+
+#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/vdpa/include/uapi/linux/virtio_ring.h b/vdpa/include/uapi/linux/virtio_ring.h
new file mode 100644
index 0000000..0e827fb
--- /dev/null
+++ b/vdpa/include/uapi/linux/virtio_ring.h
@@ -0,0 +1,248 @@
+#ifndef _LINUX_VIRTIO_RING_H
+#define _LINUX_VIRTIO_RING_H
+/* An interface for efficient virtio implementation, currently for use by KVM,
+ * but hopefully others soon. Do NOT change this since it will
+ * break existing servers and clients.
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of IBM 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 IBM 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.
+ *
+ * Copyright Rusty Russell IBM Corporation 2007. */
+#include <stdint.h>
+#include <linux/types.h>
+#include <linux/virtio_types.h>
+
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT 1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VRING_DESC_F_WRITE 2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT 4
+
+/*
+ * Mark a descriptor as available or used in packed ring.
+ * Notice: they are defined as shifts instead of shifted values.
+ */
+#define VRING_PACKED_DESC_F_AVAIL 7
+#define VRING_PACKED_DESC_F_USED 15
+
+/* The Host uses this in used->flags to advise the Guest: don't kick me when
+ * you add a buffer. It's unreliable, so it's simply an optimization. Guest
+ * will still kick if it's out of buffers. */
+#define VRING_USED_F_NO_NOTIFY 1
+/* The Guest uses this in avail->flags to advise the Host: don't interrupt me
+ * when you consume a buffer. It's unreliable, so it's simply an
+ * optimization. */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+/* Enable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_ENABLE 0x0
+/* Disable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_DISABLE 0x1
+/*
+ * Enable events for a specific descriptor in packed ring.
+ * (as specified by Descriptor Ring Change Event Offset/Wrap Counter).
+ * Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated.
+ */
+#define VRING_PACKED_EVENT_FLAG_DESC 0x2
+
+/*
+ * Wrap counter bit shift in event suppression structure
+ * of packed ring.
+ */
+#define VRING_PACKED_EVENT_F_WRAP_CTR 15
+
+/* We support indirect buffer descriptors */
+#define VIRTIO_RING_F_INDIRECT_DESC 28
+
+/* The Guest publishes the used index for which it expects an interrupt
+ * at the end of the avail ring. Host should ignore the avail->flags field. */
+/* The Host publishes the avail index for which it expects a kick
+ * at the end of the used ring. Guest should ignore the used->flags field. */
+#define VIRTIO_RING_F_EVENT_IDX 29
+
+/* Alignment requirements for vring elements.
+ * When using pre-virtio 1.0 layout, these fall out naturally.
+ */
+#define VRING_AVAIL_ALIGN_SIZE 2
+#define VRING_USED_ALIGN_SIZE 4
+#define VRING_DESC_ALIGN_SIZE 16
+
+/**
+ * struct vring_desc - Virtio ring descriptors,
+ * 16 bytes long. These can chain together via @next.
+ *
+ * @addr: buffer address (guest-physical)
+ * @len: buffer length
+ * @flags: descriptor flags
+ * @next: index of the next descriptor in the chain,
+ * if the VRING_DESC_F_NEXT flag is set. We chain unused
+ * descriptors via this, too.
+ */
+struct vring_desc {
+ __virtio64 addr;
+ __virtio32 len;
+ __virtio16 flags;
+ __virtio16 next;
+};
+
+struct vring_avail {
+ __virtio16 flags;
+ __virtio16 idx;
+ __virtio16 ring[];
+};
+
+/* u32 is used here for ids for padding reasons. */
+struct vring_used_elem {
+ /* Index of start of used descriptor chain. */
+ __virtio32 id;
+ /* Total length of the descriptor chain which was used (written to) */
+ __virtio32 len;
+};
+
+typedef struct vring_used_elem __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
+ vring_used_elem_t;
+
+struct vring_used {
+ __virtio16 flags;
+ __virtio16 idx;
+ vring_used_elem_t ring[];
+};
+
+/*
+ * The ring element addresses are passed between components with different
+ * alignments assumptions. Thus, we might need to decrease the compiler-selected
+ * alignment, and so must use a typedef to make sure the aligned attribute
+ * actually takes hold:
+ *
+ * https://gcc.gnu.org/onlinedocs//gcc/Common-Type-Attributes.html#Common-Type-Attributes
+ *
+ * When used on a struct, or struct member, the aligned attribute can only
+ * increase the alignment; in order to decrease it, the packed attribute must
+ * be specified as well. When used as part of a typedef, the aligned attribute
+ * can both increase and decrease alignment, and specifying the packed
+ * attribute generates a warning.
+ */
+typedef struct vring_desc __attribute__((aligned(VRING_DESC_ALIGN_SIZE)))
+ vring_desc_t;
+typedef struct vring_avail __attribute__((aligned(VRING_AVAIL_ALIGN_SIZE)))
+ vring_avail_t;
+typedef struct vring_used __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
+ vring_used_t;
+
+struct vring {
+ unsigned int num;
+
+ vring_desc_t *desc;
+
+ vring_avail_t *avail;
+
+ vring_used_t *used;
+};
+
+#ifndef VIRTIO_RING_NO_LEGACY
+
+/* The standard layout for the ring is a continuous chunk of memory which looks
+ * like this. We assume num is a power of 2.
+ *
+ * struct vring
+ * {
+ * // The actual descriptors (16 bytes each)
+ * struct vring_desc desc[num];
+ *
+ * // A ring of available descriptor heads with free-running index.
+ * __virtio16 avail_flags;
+ * __virtio16 avail_idx;
+ * __virtio16 available[num];
+ * __virtio16 used_event_idx;
+ *
+ * // Padding to the next align boundary.
+ * char pad[];
+ *
+ * // A ring of used descriptor heads with free-running index.
+ * __virtio16 used_flags;
+ * __virtio16 used_idx;
+ * struct vring_used_elem used[num];
+ * __virtio16 avail_event_idx;
+ * };
+ */
+/* We publish the used event index at the end of the available ring, and vice
+ * versa. They are at the end for backwards compatibility. */
+#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
+#define vring_avail_event(vr) (*(__virtio16 *)&(vr)->used->ring[(vr)->num])
+
+static __inline__ void vring_init(struct vring *vr, unsigned int num, void *p,
+ unsigned long align)
+{
+ vr->num = num;
+ vr->desc = p;
+ vr->avail = (struct vring_avail *)((char *)p + num * sizeof(struct vring_desc));
+ vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16)
+ + align-1) & ~(align - 1));
+}
+
+static __inline__ unsigned vring_size(unsigned int num, unsigned long align)
+{
+ return ((sizeof(struct vring_desc) * num + sizeof(__virtio16) * (3 + num)
+ + align - 1) & ~(align - 1))
+ + sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num;
+}
+
+#endif /* VIRTIO_RING_NO_LEGACY */
+
+/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */
+/* Assuming a given event_idx value from the other side, if
+ * we have just incremented index from old to new_idx,
+ * should we trigger an event? */
+static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
+{
+ /* Note: Xen has similar logic for notification hold-off
+ * in include/xen/interface/io/ring.h with req_event and req_prod
+ * corresponding to event_idx + 1 and new_idx respectively.
+ * Note also that req_event and req_prod in Xen start at 1,
+ * event indexes in virtio start at 0. */
+ return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
+}
+
+struct vring_packed_desc_event {
+ /* Descriptor Ring Change Event Offset/Wrap Counter. */
+ __le16 off_wrap;
+ /* Descriptor Ring Change Event Flags. */
+ __le16 flags;
+};
+
+struct vring_packed_desc {
+ /* Buffer Address. */
+ __le64 addr;
+ /* Buffer Length. */
+ __le32 len;
+ /* Buffer ID. */
+ __le16 id;
+ /* The flags depending on descriptor type. */
+ __le16 flags;
+};
+
+#endif /* _LINUX_VIRTIO_RING_H */
diff --git a/vdpa/vdpa.c b/vdpa/vdpa.c
new file mode 100644
index 0000000..b73e40b
--- /dev/null
+++ b/vdpa/vdpa.c
@@ -0,0 +1,1139 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <linux/genetlink.h>
+#include <linux/if_ether.h>
+#include <linux/vdpa.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_net.h>
+#include <linux/netlink.h>
+#include <libmnl/libmnl.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_config.h>
+#include "mnl_utils.h"
+#include <rt_names.h>
+
+#include "version.h"
+#include "json_print.h"
+#include "utils.h"
+
+#define VDPA_OPT_MGMTDEV_HANDLE BIT(0)
+#define VDPA_OPT_VDEV_MGMTDEV_HANDLE BIT(1)
+#define VDPA_OPT_VDEV_NAME BIT(2)
+#define VDPA_OPT_VDEV_HANDLE BIT(3)
+#define VDPA_OPT_VDEV_MAC BIT(4)
+#define VDPA_OPT_VDEV_MTU BIT(5)
+#define VDPA_OPT_MAX_VQP BIT(6)
+#define VDPA_OPT_QUEUE_INDEX BIT(7)
+
+struct vdpa_opts {
+ uint64_t present; /* flags of present items */
+ char *mdev_bus_name;
+ char *mdev_name;
+ const char *vdev_name;
+ unsigned int device_id;
+ char mac[ETH_ALEN];
+ uint16_t mtu;
+ uint16_t max_vqp;
+ uint32_t queue_idx;
+};
+
+struct vdpa {
+ struct mnlu_gen_socket nlg;
+ struct vdpa_opts opts;
+ bool json_output;
+ struct indent_mem *indent;
+};
+
+static void pr_out_section_start(struct vdpa *vdpa, const char *name)
+{
+ open_json_object(NULL);
+ open_json_object(name);
+}
+
+static void pr_out_section_end(struct vdpa *vdpa)
+{
+ close_json_object();
+ close_json_object();
+}
+
+static void pr_out_array_start(struct vdpa *vdpa, const char *name)
+{
+ if (!vdpa->json_output) {
+ print_nl();
+ inc_indent(vdpa->indent);
+ print_indent(vdpa->indent);
+ }
+ open_json_array(PRINT_ANY, name);
+}
+
+static void pr_out_array_end(struct vdpa *vdpa)
+{
+ close_json_array(PRINT_JSON, NULL);
+ if (!vdpa->json_output)
+ dec_indent(vdpa->indent);
+}
+
+static const enum mnl_attr_data_type vdpa_policy[VDPA_ATTR_MAX + 1] = {
+ [VDPA_ATTR_MGMTDEV_BUS_NAME] = MNL_TYPE_NUL_STRING,
+ [VDPA_ATTR_MGMTDEV_DEV_NAME] = MNL_TYPE_NUL_STRING,
+ [VDPA_ATTR_DEV_NAME] = MNL_TYPE_STRING,
+ [VDPA_ATTR_DEV_ID] = MNL_TYPE_U32,
+ [VDPA_ATTR_DEV_VENDOR_ID] = MNL_TYPE_U32,
+ [VDPA_ATTR_DEV_MAX_VQS] = MNL_TYPE_U32,
+ [VDPA_ATTR_DEV_MAX_VQ_SIZE] = MNL_TYPE_U16,
+ [VDPA_ATTR_DEV_NEGOTIATED_FEATURES] = MNL_TYPE_U64,
+ [VDPA_ATTR_DEV_MGMTDEV_MAX_VQS] = MNL_TYPE_U32,
+ [VDPA_ATTR_DEV_SUPPORTED_FEATURES] = MNL_TYPE_U64,
+};
+
+static int attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type;
+
+ if (mnl_attr_type_valid(attr, VDPA_ATTR_MAX) < 0)
+ return MNL_CB_OK;
+
+ type = mnl_attr_get_type(attr);
+ if (mnl_attr_validate(attr, vdpa_policy[type]) < 0)
+ return MNL_CB_ERROR;
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int vdpa_argv_handle(struct vdpa *vdpa, int argc, char **argv,
+ char **p_mdev_bus_name,
+ char **p_mdev_name)
+{
+ unsigned int slashcount;
+ char *str;
+
+ if (argc <= 0 || *argv == NULL) {
+ fprintf(stderr,
+ "vdpa identification (\"mgmtdev_bus_name/mgmtdev_name\") expected\n");
+ return -EINVAL;
+ }
+ str = *argv;
+ slashcount = get_str_char_count(str, '/');
+ if (slashcount > 1) {
+ fprintf(stderr,
+ "Wrong vdpa mgmtdev identification string format\n");
+ fprintf(stderr, "Expected \"mgmtdev_bus_name/mgmtdev_name\"\n");
+ fprintf(stderr, "Expected \"mgmtdev_name\"\n");
+ return -EINVAL;
+ }
+ switch (slashcount) {
+ case 0:
+ *p_mdev_bus_name = NULL;
+ *p_mdev_name = str;
+ return 0;
+ case 1:
+ str_split_by_char(str, p_mdev_bus_name, p_mdev_name, '/');
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vdpa_argv_str(struct vdpa *vdpa, int argc, char **argv,
+ const char **p_str)
+{
+ if (argc <= 0 || *argv == NULL) {
+ fprintf(stderr, "String parameter expected\n");
+ return -EINVAL;
+ }
+ *p_str = *argv;
+ return 0;
+}
+
+static int vdpa_argv_mac(struct vdpa *vdpa, int argc, char **argv, char *mac)
+{
+ int alen;
+
+ if (argc <= 0 || *argv == NULL) {
+ fprintf(stderr, "String parameter expected\n");
+ return -EINVAL;
+ }
+
+ alen = ll_addr_a2n(mac, ETH_ALEN, *argv);
+ if (alen < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vdpa_argv_u16(struct vdpa *vdpa, int argc, char **argv,
+ uint16_t *result)
+{
+ if (argc <= 0 || *argv == NULL) {
+ fprintf(stderr, "number expected\n");
+ return -EINVAL;
+ }
+
+ return get_u16(result, *argv, 10);
+}
+
+static int vdpa_argv_u32(struct vdpa *vdpa, int argc, char **argv,
+ uint32_t *result)
+{
+ if (argc <= 0 || !*argv) {
+ fprintf(stderr, "number expected\n");
+ return -EINVAL;
+ }
+
+ return get_u32(result, *argv, 10);
+}
+
+struct vdpa_args_metadata {
+ uint64_t o_flag;
+ const char *err_msg;
+};
+
+static const struct vdpa_args_metadata vdpa_args_required[] = {
+ {VDPA_OPT_VDEV_MGMTDEV_HANDLE, "management device handle not set."},
+ {VDPA_OPT_VDEV_NAME, "device name is not set."},
+ {VDPA_OPT_VDEV_HANDLE, "device name is not set."},
+ {VDPA_OPT_QUEUE_INDEX, "queue index is not set."},
+};
+
+static int vdpa_args_finding_required_validate(uint64_t o_required,
+ uint64_t o_found)
+{
+ uint64_t o_flag;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vdpa_args_required); i++) {
+ o_flag = vdpa_args_required[i].o_flag;
+ if ((o_required & o_flag) && !(o_found & o_flag)) {
+ fprintf(stderr, "%s\n", vdpa_args_required[i].err_msg);
+ return -EINVAL;
+ }
+ }
+ if (o_required & ~o_found) {
+ fprintf(stderr,
+ "BUG: unknown argument required but not found\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void vdpa_opts_put(struct nlmsghdr *nlh, struct vdpa *vdpa)
+{
+ struct vdpa_opts *opts = &vdpa->opts;
+
+ if ((opts->present & VDPA_OPT_MGMTDEV_HANDLE) ||
+ (opts->present & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
+ if (opts->mdev_bus_name)
+ mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_BUS_NAME,
+ opts->mdev_bus_name);
+ mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_DEV_NAME,
+ opts->mdev_name);
+ }
+ if ((opts->present & VDPA_OPT_VDEV_NAME) ||
+ (opts->present & VDPA_OPT_VDEV_HANDLE))
+ mnl_attr_put_strz(nlh, VDPA_ATTR_DEV_NAME, opts->vdev_name);
+ if (opts->present & VDPA_OPT_VDEV_MAC)
+ mnl_attr_put(nlh, VDPA_ATTR_DEV_NET_CFG_MACADDR,
+ sizeof(opts->mac), opts->mac);
+ if (opts->present & VDPA_OPT_VDEV_MTU)
+ mnl_attr_put_u16(nlh, VDPA_ATTR_DEV_NET_CFG_MTU, opts->mtu);
+ if (opts->present & VDPA_OPT_MAX_VQP)
+ mnl_attr_put_u16(nlh, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, opts->max_vqp);
+ if (opts->present & VDPA_OPT_QUEUE_INDEX)
+ mnl_attr_put_u32(nlh, VDPA_ATTR_DEV_QUEUE_INDEX, opts->queue_idx);
+}
+
+static int vdpa_argv_parse(struct vdpa *vdpa, int argc, char **argv,
+ uint64_t o_required, uint64_t o_optional)
+{
+ uint64_t o_all = o_required | o_optional;
+ struct vdpa_opts *opts = &vdpa->opts;
+ uint64_t o_found = 0;
+ int err;
+
+ if (o_required & VDPA_OPT_MGMTDEV_HANDLE) {
+ err = vdpa_argv_handle(vdpa, argc, argv, &opts->mdev_bus_name,
+ &opts->mdev_name);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_MGMTDEV_HANDLE;
+ } else if (o_required & VDPA_OPT_VDEV_HANDLE) {
+ err = vdpa_argv_str(vdpa, argc, argv, &opts->vdev_name);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_VDEV_HANDLE;
+ }
+
+ while (NEXT_ARG_OK()) {
+ if ((matches(*argv, "name") == 0) &&
+ (o_all & VDPA_OPT_VDEV_NAME)) {
+ const char *namestr;
+
+ NEXT_ARG_FWD();
+ err = vdpa_argv_str(vdpa, argc, argv, &namestr);
+ if (err)
+ return err;
+ opts->vdev_name = namestr;
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_VDEV_NAME;
+ } else if ((matches(*argv, "mgmtdev") == 0) &&
+ (o_all & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
+ NEXT_ARG_FWD();
+ err = vdpa_argv_handle(vdpa, argc, argv,
+ &opts->mdev_bus_name,
+ &opts->mdev_name);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_VDEV_MGMTDEV_HANDLE;
+ } else if ((strcmp(*argv, "mac") == 0) &&
+ (o_all & VDPA_OPT_VDEV_MAC)) {
+ NEXT_ARG_FWD();
+ err = vdpa_argv_mac(vdpa, argc, argv, opts->mac);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_VDEV_MAC;
+ } else if ((strcmp(*argv, "mtu") == 0) &&
+ (o_all & VDPA_OPT_VDEV_MTU)) {
+ NEXT_ARG_FWD();
+ err = vdpa_argv_u16(vdpa, argc, argv, &opts->mtu);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_VDEV_MTU;
+ } else if ((matches(*argv, "max_vqp") == 0) && (o_optional & VDPA_OPT_MAX_VQP)) {
+ NEXT_ARG_FWD();
+ err = vdpa_argv_u16(vdpa, argc, argv, &opts->max_vqp);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_MAX_VQP;
+ } else if (!strcmp(*argv, "qidx") &&
+ (o_optional & VDPA_OPT_QUEUE_INDEX)) {
+ NEXT_ARG_FWD();
+ err = vdpa_argv_u32(vdpa, argc, argv, &opts->queue_idx);
+ if (err)
+ return err;
+
+ NEXT_ARG_FWD();
+ o_found |= VDPA_OPT_QUEUE_INDEX;
+ } else {
+ fprintf(stderr, "Unknown option \"%s\"\n", *argv);
+ return -EINVAL;
+ }
+ }
+
+ opts->present = o_found;
+
+ return vdpa_args_finding_required_validate(o_required, o_found);
+}
+
+static int vdpa_argv_parse_put(struct nlmsghdr *nlh, struct vdpa *vdpa,
+ int argc, char **argv,
+ uint64_t o_required, uint64_t o_optional)
+{
+ int err;
+
+ err = vdpa_argv_parse(vdpa, argc, argv, o_required, o_optional);
+ if (err)
+ return err;
+ vdpa_opts_put(nlh, vdpa);
+ return 0;
+}
+
+static void cmd_mgmtdev_help(void)
+{
+ fprintf(stderr, "Usage: vdpa mgmtdev show [ DEV ]\n");
+}
+
+static void pr_out_handle_start(struct vdpa *vdpa, struct nlattr **tb)
+{
+ const char *mdev_bus_name = NULL;
+ const char *mdev_name;
+ SPRINT_BUF(buf);
+
+ mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
+ if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME]) {
+ mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
+ sprintf(buf, "%s/%s", mdev_bus_name, mdev_name);
+ } else {
+ sprintf(buf, "%s", mdev_name);
+ }
+
+ if (vdpa->json_output)
+ open_json_object(buf);
+ else
+ printf("%s: ", buf);
+}
+
+static void pr_out_handle_end(struct vdpa *vdpa)
+{
+ if (vdpa->json_output)
+ close_json_object();
+ else
+ print_nl();
+}
+
+static void __pr_out_vdev_handle_start(struct vdpa *vdpa, const char *vdev_name)
+{
+ SPRINT_BUF(buf);
+
+ sprintf(buf, "%s", vdev_name);
+ if (vdpa->json_output)
+ open_json_object(buf);
+ else
+ printf("%s: ", buf);
+}
+
+static void pr_out_vdev_handle_start(struct vdpa *vdpa, struct nlattr **tb)
+{
+ const char *vdev_name;
+
+ vdev_name = mnl_attr_get_str(tb[VDPA_ATTR_DEV_NAME]);
+ __pr_out_vdev_handle_start(vdpa, vdev_name);
+}
+
+static void pr_out_vdev_handle_end(struct vdpa *vdpa)
+{
+ if (vdpa->json_output)
+ close_json_object();
+ else
+ print_nl();
+}
+
+static struct str_num_map class_map[] = {
+ { .str = "net", .num = VIRTIO_ID_NET },
+ { .str = "block", .num = VIRTIO_ID_BLOCK },
+ { .str = NULL, },
+};
+
+static const char *parse_class(int num)
+{
+ const char *class;
+
+ class = str_map_lookup_uint(class_map, num);
+ return class ? class : "< unknown class >";
+}
+
+static const char * const net_feature_strs[64] = {
+ [VIRTIO_NET_F_CSUM] = "CSUM",
+ [VIRTIO_NET_F_GUEST_CSUM] = "GUEST_CSUM",
+ [VIRTIO_NET_F_CTRL_GUEST_OFFLOADS] = "CTRL_GUEST_OFFLOADS",
+ [VIRTIO_NET_F_MTU] = "MTU",
+ [VIRTIO_NET_F_MAC] = "MAC",
+ [VIRTIO_NET_F_GUEST_TSO4] = "GUEST_TSO4",
+ [VIRTIO_NET_F_GUEST_TSO6] = "GUEST_TSO6",
+ [VIRTIO_NET_F_GUEST_ECN] = "GUEST_ECN",
+ [VIRTIO_NET_F_GUEST_UFO] = "GUEST_UFO",
+ [VIRTIO_NET_F_HOST_TSO4] = "HOST_TSO4",
+ [VIRTIO_NET_F_HOST_TSO6] = "HOST_TSO6",
+ [VIRTIO_NET_F_HOST_ECN] = "HOST_ECN",
+ [VIRTIO_NET_F_HOST_UFO] = "HOST_UFO",
+ [VIRTIO_NET_F_MRG_RXBUF] = "MRG_RXBUF",
+ [VIRTIO_NET_F_STATUS] = "STATUS",
+ [VIRTIO_NET_F_CTRL_VQ] = "CTRL_VQ",
+ [VIRTIO_NET_F_CTRL_RX] = "CTRL_RX",
+ [VIRTIO_NET_F_CTRL_VLAN] = "CTRL_VLAN",
+ [VIRTIO_NET_F_CTRL_RX_EXTRA] = "CTRL_RX_EXTRA",
+ [VIRTIO_NET_F_GUEST_ANNOUNCE] = "GUEST_ANNOUNCE",
+ [VIRTIO_NET_F_MQ] = "MQ",
+ [VIRTIO_F_NOTIFY_ON_EMPTY] = "NOTIFY_ON_EMPTY",
+ [VIRTIO_NET_F_CTRL_MAC_ADDR] = "CTRL_MAC_ADDR",
+ [VIRTIO_F_ANY_LAYOUT] = "ANY_LAYOUT",
+ [VIRTIO_NET_F_RSC_EXT] = "RSC_EXT",
+ [VIRTIO_NET_F_HASH_REPORT] = "HASH_REPORT",
+ [VIRTIO_NET_F_RSS] = "RSS",
+ [VIRTIO_NET_F_STANDBY] = "STANDBY",
+ [VIRTIO_NET_F_SPEED_DUPLEX] = "SPEED_DUPLEX",
+};
+
+#define VIRTIO_F_IN_ORDER 35
+#define VIRTIO_F_NOTIFICATION_DATA 38
+#define VDPA_EXT_FEATURES_SZ (VIRTIO_TRANSPORT_F_END - \
+ VIRTIO_TRANSPORT_F_START + 1)
+
+static const char * const ext_feature_strs[VDPA_EXT_FEATURES_SZ] = {
+ [VIRTIO_RING_F_INDIRECT_DESC - VIRTIO_TRANSPORT_F_START] = "RING_INDIRECT_DESC",
+ [VIRTIO_RING_F_EVENT_IDX - VIRTIO_TRANSPORT_F_START] = "RING_EVENT_IDX",
+ [VIRTIO_F_VERSION_1 - VIRTIO_TRANSPORT_F_START] = "VERSION_1",
+ [VIRTIO_F_ACCESS_PLATFORM - VIRTIO_TRANSPORT_F_START] = "ACCESS_PLATFORM",
+ [VIRTIO_F_RING_PACKED - VIRTIO_TRANSPORT_F_START] = "RING_PACKED",
+ [VIRTIO_F_IN_ORDER - VIRTIO_TRANSPORT_F_START] = "IN_ORDER",
+ [VIRTIO_F_ORDER_PLATFORM - VIRTIO_TRANSPORT_F_START] = "ORDER_PLATFORM",
+ [VIRTIO_F_SR_IOV - VIRTIO_TRANSPORT_F_START] = "SR_IOV",
+ [VIRTIO_F_NOTIFICATION_DATA - VIRTIO_TRANSPORT_F_START] = "NOTIFICATION_DATA",
+};
+
+static const char * const *dev_to_feature_str[] = {
+ [VIRTIO_ID_NET] = net_feature_strs,
+};
+
+#define NUM_FEATURE_BITS 64
+
+static void print_features(struct vdpa *vdpa, uint64_t features, bool mgmtdevf,
+ uint16_t dev_id)
+{
+ const char * const *feature_strs = NULL;
+ const char *s;
+ int i;
+
+ if (dev_id < ARRAY_SIZE(dev_to_feature_str))
+ feature_strs = dev_to_feature_str[dev_id];
+
+ if (mgmtdevf)
+ pr_out_array_start(vdpa, "dev_features");
+ else
+ pr_out_array_start(vdpa, "negotiated_features");
+
+ for (i = 0; i < NUM_FEATURE_BITS; i++) {
+ if (!(features & (1ULL << i)))
+ continue;
+
+ if (i < VIRTIO_TRANSPORT_F_START || i > VIRTIO_TRANSPORT_F_END)
+ s = feature_strs ? feature_strs[i] : NULL;
+ else
+ s = ext_feature_strs[i - VIRTIO_TRANSPORT_F_START];
+
+ if (!s)
+ print_uint(PRINT_ANY, NULL, " bit_%d", i);
+ else
+ print_string(PRINT_ANY, NULL, " %s", s);
+ }
+
+ pr_out_array_end(vdpa);
+}
+
+static void pr_out_mgmtdev_show(struct vdpa *vdpa, const struct nlmsghdr *nlh,
+ struct nlattr **tb)
+{
+ uint64_t classes = 0;
+ const char *class;
+ unsigned int i;
+
+ pr_out_handle_start(vdpa, tb);
+
+ if (tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]) {
+ classes = mnl_attr_get_u64(tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]);
+ pr_out_array_start(vdpa, "supported_classes");
+
+ for (i = 1; i < 64; i++) {
+ if ((classes & (1ULL << i)) == 0)
+ continue;
+
+ class = parse_class(i);
+ print_string(PRINT_ANY, NULL, " %s", class);
+ }
+ pr_out_array_end(vdpa);
+ }
+
+ if (tb[VDPA_ATTR_DEV_MGMTDEV_MAX_VQS]) {
+ uint32_t num_vqs;
+
+ print_nl();
+ num_vqs = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_MGMTDEV_MAX_VQS]);
+ print_uint(PRINT_ANY, "max_supported_vqs", " max_supported_vqs %d", num_vqs);
+ }
+
+ if (tb[VDPA_ATTR_DEV_SUPPORTED_FEATURES]) {
+ uint64_t features;
+
+ features = mnl_attr_get_u64(tb[VDPA_ATTR_DEV_SUPPORTED_FEATURES]);
+ if (classes & BIT(VIRTIO_ID_NET))
+ print_features(vdpa, features, true, VIRTIO_ID_NET);
+ else
+ print_features(vdpa, features, true, 0);
+ }
+
+ pr_out_handle_end(vdpa);
+}
+
+static int cmd_mgmtdev_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
+ struct vdpa *vdpa = data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+
+ if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME])
+ return MNL_CB_ERROR;
+
+ pr_out_mgmtdev_show(vdpa, nlh, tb);
+
+ return MNL_CB_OK;
+}
+
+static int cmd_mgmtdev_show(struct vdpa *vdpa, int argc, char **argv)
+{
+ uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nlmsghdr *nlh;
+ int err;
+
+ if (argc == 0)
+ flags |= NLM_F_DUMP;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_MGMTDEV_GET,
+ flags);
+ if (argc > 0) {
+ err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
+ VDPA_OPT_MGMTDEV_HANDLE, 0);
+ if (err)
+ return err;
+ }
+
+ pr_out_section_start(vdpa, "mgmtdev");
+ err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_mgmtdev_show_cb, vdpa);
+ pr_out_section_end(vdpa);
+ return err;
+}
+
+static int cmd_mgmtdev(struct vdpa *vdpa, int argc, char **argv)
+{
+ if (!argc || matches(*argv, "help") == 0) {
+ cmd_mgmtdev_help();
+ return 0;
+ } else if (matches(*argv, "show") == 0 ||
+ matches(*argv, "list") == 0) {
+ return cmd_mgmtdev_show(vdpa, argc - 1, argv + 1);
+ }
+ fprintf(stderr, "Command \"%s\" not found\n", *argv);
+ return -ENOENT;
+}
+
+static void cmd_dev_help(void)
+{
+ fprintf(stderr, "Usage: vdpa dev show [ DEV ]\n");
+ fprintf(stderr, " vdpa dev add name NAME mgmtdev MANAGEMENTDEV [ mac MACADDR ] [ mtu MTU ]\n");
+ fprintf(stderr, " [ max_vqp MAX_VQ_PAIRS ]\n");
+ fprintf(stderr, " vdpa dev del DEV\n");
+ fprintf(stderr, "Usage: vdpa dev config COMMAND [ OPTIONS ]\n");
+ fprintf(stderr, "Usage: vdpa dev vstats COMMAND\n");
+}
+
+static const char *device_type_name(uint32_t type)
+{
+ switch (type) {
+ case 0x1: return "network";
+ case 0x2: return "block";
+ default: return "<unknown type>";
+ }
+}
+
+static void pr_out_dev(struct vdpa *vdpa, struct nlattr **tb)
+{
+ const char *mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
+ uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
+ const char *mdev_bus_name = NULL;
+ char mgmtdev_buf[128];
+
+ if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME])
+ mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
+
+ if (mdev_bus_name)
+ sprintf(mgmtdev_buf, "%s/%s", mdev_bus_name, mdev_name);
+ else
+ sprintf(mgmtdev_buf, "%s", mdev_name);
+ pr_out_vdev_handle_start(vdpa, tb);
+ print_string(PRINT_ANY, "type", "type %s", device_type_name(device_id));
+ print_string(PRINT_ANY, "mgmtdev", " mgmtdev %s", mgmtdev_buf);
+
+ if (tb[VDPA_ATTR_DEV_VENDOR_ID])
+ print_uint(PRINT_ANY, "vendor_id", " vendor_id %u",
+ mnl_attr_get_u32(tb[VDPA_ATTR_DEV_VENDOR_ID]));
+ if (tb[VDPA_ATTR_DEV_MAX_VQS])
+ print_uint(PRINT_ANY, "max_vqs", " max_vqs %u",
+ mnl_attr_get_u32(tb[VDPA_ATTR_DEV_MAX_VQS]));
+ if (tb[VDPA_ATTR_DEV_MAX_VQ_SIZE])
+ print_uint(PRINT_ANY, "max_vq_size", " max_vq_size %u",
+ mnl_attr_get_u16(tb[VDPA_ATTR_DEV_MAX_VQ_SIZE]));
+ pr_out_vdev_handle_end(vdpa);
+}
+
+static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
+ struct vdpa *vdpa = data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME] ||
+ !tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
+ return MNL_CB_ERROR;
+ pr_out_dev(vdpa, tb);
+ return MNL_CB_OK;
+}
+
+static int cmd_dev_show(struct vdpa *vdpa, int argc, char **argv)
+{
+ uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nlmsghdr *nlh;
+ int err;
+
+ if (argc <= 0)
+ flags |= NLM_F_DUMP;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_GET, flags);
+ if (argc > 0) {
+ err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
+ VDPA_OPT_VDEV_HANDLE, 0);
+ if (err)
+ return err;
+ }
+
+ pr_out_section_start(vdpa, "dev");
+ err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_show_cb, vdpa);
+ pr_out_section_end(vdpa);
+ return err;
+}
+
+static int cmd_dev_add(struct vdpa *vdpa, int argc, char **argv)
+{
+ struct nlmsghdr *nlh;
+ int err;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_NEW,
+ NLM_F_REQUEST | NLM_F_ACK);
+ err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
+ VDPA_OPT_VDEV_MGMTDEV_HANDLE | VDPA_OPT_VDEV_NAME,
+ VDPA_OPT_VDEV_MAC | VDPA_OPT_VDEV_MTU |
+ VDPA_OPT_MAX_VQP);
+ if (err)
+ return err;
+
+ return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_dev_del(struct vdpa *vdpa, int argc, char **argv)
+{
+ struct nlmsghdr *nlh;
+ int err;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_DEL,
+ NLM_F_REQUEST | NLM_F_ACK);
+ err = vdpa_argv_parse_put(nlh, vdpa, argc, argv, VDPA_OPT_VDEV_HANDLE,
+ 0);
+ if (err)
+ return err;
+
+ return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
+}
+
+static void pr_out_dev_net_config(struct vdpa *vdpa, struct nlattr **tb)
+{
+ SPRINT_BUF(macaddr);
+ uint64_t val_u64;
+ uint16_t val_u16;
+
+ if (tb[VDPA_ATTR_DEV_NET_CFG_MACADDR]) {
+ const unsigned char *data;
+ uint16_t len;
+
+ len = mnl_attr_get_payload_len(tb[VDPA_ATTR_DEV_NET_CFG_MACADDR]);
+ data = mnl_attr_get_payload(tb[VDPA_ATTR_DEV_NET_CFG_MACADDR]);
+
+ print_string(PRINT_ANY, "mac", "mac %s ",
+ ll_addr_n2a(data, len, 0, macaddr, sizeof(macaddr)));
+ }
+ if (tb[VDPA_ATTR_DEV_NET_STATUS]) {
+ val_u16 = mnl_attr_get_u16(tb[VDPA_ATTR_DEV_NET_STATUS]);
+ print_string(PRINT_ANY, "link ", "link %s ",
+ (val_u16 & VIRTIO_NET_S_LINK_UP) ? "up" : "down");
+ print_bool(PRINT_ANY, "link_announce ", "link_announce %s ",
+ (val_u16 & VIRTIO_NET_S_ANNOUNCE) ? true : false);
+ }
+ if (tb[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]) {
+ val_u16 = mnl_attr_get_u16(tb[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]);
+ print_uint(PRINT_ANY, "max_vq_pairs", "max_vq_pairs %d ",
+ val_u16);
+ }
+ if (tb[VDPA_ATTR_DEV_NET_CFG_MTU]) {
+ val_u16 = mnl_attr_get_u16(tb[VDPA_ATTR_DEV_NET_CFG_MTU]);
+ print_uint(PRINT_ANY, "mtu", "mtu %d ", val_u16);
+ }
+ if (tb[VDPA_ATTR_DEV_NEGOTIATED_FEATURES]) {
+ uint16_t dev_id = 0;
+
+ if (tb[VDPA_ATTR_DEV_ID])
+ dev_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
+
+ val_u64 = mnl_attr_get_u64(tb[VDPA_ATTR_DEV_NEGOTIATED_FEATURES]);
+ print_features(vdpa, val_u64, false, dev_id);
+ }
+}
+
+static void pr_out_dev_config(struct vdpa *vdpa, struct nlattr **tb)
+{
+ uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
+
+ pr_out_vdev_handle_start(vdpa, tb);
+ switch (device_id) {
+ case VIRTIO_ID_NET:
+ pr_out_dev_net_config(vdpa, tb);
+ break;
+ default:
+ break;
+ }
+ pr_out_vdev_handle_end(vdpa);
+}
+
+static int cmd_dev_config_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
+ struct vdpa *vdpa = data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
+ return MNL_CB_ERROR;
+ pr_out_dev_config(vdpa, tb);
+ return MNL_CB_OK;
+}
+
+static int cmd_dev_config_show(struct vdpa *vdpa, int argc, char **argv)
+{
+ uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nlmsghdr *nlh;
+ int err;
+
+ if (argc <= 0)
+ flags |= NLM_F_DUMP;
+
+ nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_CONFIG_GET,
+ flags);
+ if (argc > 0) {
+ err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
+ VDPA_OPT_VDEV_HANDLE, 0);
+ if (err)
+ return err;
+ }
+
+ pr_out_section_start(vdpa, "config");
+ err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_config_show_cb, vdpa);
+ pr_out_section_end(vdpa);
+ return err;
+}
+
+static void cmd_dev_config_help(void)
+{
+ fprintf(stderr, "Usage: vdpa dev config show [ DEV ]\n");
+}
+
+static int cmd_dev_config(struct vdpa *vdpa, int argc, char **argv)
+{
+ if (!argc)
+ return cmd_dev_config_show(vdpa, argc - 1, argv + 1);
+
+ if (matches(*argv, "help") == 0) {
+ cmd_dev_config_help();
+ return 0;
+ } else if (matches(*argv, "show") == 0) {
+ return cmd_dev_config_show(vdpa, argc - 1, argv + 1);
+ }
+ fprintf(stderr, "Command \"%s\" not found\n", *argv);
+ return -ENOENT;
+}
+
+#define MAX_KEY_LEN 200
+/* 5 bytes for format */
+#define MAX_FMT_LEN (MAX_KEY_LEN + 5 + 1)
+
+static void print_queue_type(struct nlattr *attr, uint16_t max_vqp, uint64_t features)
+{
+ bool is_ctrl = false;
+ uint16_t qidx = 0;
+
+ qidx = mnl_attr_get_u16(attr);
+ is_ctrl = features & BIT(VIRTIO_NET_F_CTRL_VQ) && qidx == 2 * max_vqp;
+ if (!is_ctrl) {
+ if (qidx & 1)
+ print_string(PRINT_ANY, "queue_type", "queue_type %s ",
+ "tx");
+ else
+ print_string(PRINT_ANY, "queue_type", "queue_type %s ",
+ "rx");
+ } else {
+ print_string(PRINT_ANY, "queue_type", "queue_type %s ",
+ "control_vq");
+ }
+}
+
+static void pr_out_dev_net_vstats(const struct nlmsghdr *nlh)
+{
+ const char *name = NULL;
+ uint64_t features = 0;
+ char fmt[MAX_FMT_LEN];
+ uint16_t max_vqp = 0;
+ struct nlattr *attr;
+ uint64_t v64;
+
+ mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
+ switch (attr->nla_type) {
+ case VDPA_ATTR_DEV_NET_CFG_MAX_VQP:
+ max_vqp = mnl_attr_get_u16(attr);
+ break;
+ case VDPA_ATTR_DEV_NEGOTIATED_FEATURES:
+ features = mnl_attr_get_u64(attr);
+ break;
+ case VDPA_ATTR_DEV_QUEUE_INDEX:
+ print_queue_type(attr, max_vqp, features);
+ break;
+ case VDPA_ATTR_DEV_VENDOR_ATTR_NAME:
+ name = mnl_attr_get_str(attr);
+ if (strlen(name) > MAX_KEY_LEN)
+ return;
+
+ strcpy(fmt, name);
+ strcat(fmt, " %lu ");
+ break;
+ case VDPA_ATTR_DEV_VENDOR_ATTR_VALUE:
+ v64 = mnl_attr_get_u64(attr);
+ print_u64(PRINT_ANY, name, fmt, v64);
+ break;
+ }
+ }
+}
+
+static void pr_out_dev_vstats(struct vdpa *vdpa, struct nlattr **tb, const struct nlmsghdr *nlh)
+{
+ uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
+
+ pr_out_vdev_handle_start(vdpa, tb);
+ switch (device_id) {
+ case VIRTIO_ID_NET:
+ pr_out_dev_net_vstats(nlh);
+ break;
+ default:
+ break;
+ }
+ pr_out_vdev_handle_end(vdpa);
+}
+
+static int cmd_dev_vstats_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
+ struct vdpa *vdpa = data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
+ return MNL_CB_ERROR;
+ pr_out_dev_vstats(vdpa, tb, nlh);
+ return MNL_CB_OK;
+}
+
+static void cmd_dev_vstats_help(void)
+{
+ fprintf(stderr, "Usage: vdpa dev vstats show DEV [qidx QUEUE_INDEX]\n");
+}
+
+static int cmd_dev_vstats_show(struct vdpa *vdpa, int argc, char **argv)
+{
+ uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nlmsghdr *nlh;
+ int err;
+
+ if (argc != 1 && argc != 3) {
+ cmd_dev_vstats_help();
+ return -EINVAL;
+ }
+
+ nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_VSTATS_GET,
+ flags);
+
+ err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
+ VDPA_OPT_VDEV_HANDLE, VDPA_OPT_QUEUE_INDEX);
+ if (err)
+ return err;
+
+ pr_out_section_start(vdpa, "vstats");
+ err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_vstats_show_cb, vdpa);
+ pr_out_section_end(vdpa);
+ return 0;
+}
+
+static int cmd_dev_vstats(struct vdpa *vdpa, int argc, char **argv)
+{
+ if (argc < 1) {
+ cmd_dev_vstats_help();
+ return -EINVAL;
+ }
+
+ if (!strcmp(*argv, "help")) {
+ cmd_dev_vstats_help();
+ return 0;
+ } else if (!strcmp(*argv, "show")) {
+ return cmd_dev_vstats_show(vdpa, argc - 1, argv + 1);
+ }
+ fprintf(stderr, "Command \"%s\" not found\n", *argv);
+ return -ENOENT;
+}
+
+static int cmd_dev(struct vdpa *vdpa, int argc, char **argv)
+{
+ if (!argc)
+ return cmd_dev_show(vdpa, argc - 1, argv + 1);
+
+ if (matches(*argv, "help") == 0) {
+ cmd_dev_help();
+ return 0;
+ } else if (matches(*argv, "show") == 0 ||
+ matches(*argv, "list") == 0) {
+ return cmd_dev_show(vdpa, argc - 1, argv + 1);
+ } else if (matches(*argv, "add") == 0) {
+ return cmd_dev_add(vdpa, argc - 1, argv + 1);
+ } else if (matches(*argv, "del") == 0) {
+ return cmd_dev_del(vdpa, argc - 1, argv + 1);
+ } else if (matches(*argv, "config") == 0) {
+ return cmd_dev_config(vdpa, argc - 1, argv + 1);
+ } else if (!strcmp(*argv, "vstats")) {
+ return cmd_dev_vstats(vdpa, argc - 1, argv + 1);
+ }
+ fprintf(stderr, "Command \"%s\" not found\n", *argv);
+ return -ENOENT;
+}
+
+static void help(void)
+{
+ fprintf(stderr,
+ "Usage: vdpa [ OPTIONS ] OBJECT { COMMAND | help }\n"
+ "where OBJECT := { mgmtdev | dev }\n"
+ " OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] }\n");
+}
+
+static int vdpa_cmd(struct vdpa *vdpa, int argc, char **argv)
+{
+ if (!argc || matches(*argv, "help") == 0) {
+ help();
+ return 0;
+ } else if (matches(*argv, "mgmtdev") == 0) {
+ return cmd_mgmtdev(vdpa, argc - 1, argv + 1);
+ } else if (matches(*argv, "dev") == 0) {
+ return cmd_dev(vdpa, argc - 1, argv + 1);
+ }
+ fprintf(stderr, "Object \"%s\" not found\n", *argv);
+ return -ENOENT;
+}
+
+static int vdpa_init(struct vdpa *vdpa)
+{
+ int err;
+
+ err = mnlu_gen_socket_open(&vdpa->nlg, VDPA_GENL_NAME,
+ VDPA_GENL_VERSION);
+ if (err) {
+ fprintf(stderr, "Failed to connect to vdpa Netlink\n");
+ return -errno;
+ }
+ new_json_obj_plain(vdpa->json_output);
+ return 0;
+}
+
+static void vdpa_fini(struct vdpa *vdpa)
+{
+ delete_json_obj_plain();
+ mnlu_gen_socket_close(&vdpa->nlg);
+}
+
+static struct vdpa *vdpa_alloc(void)
+{
+ struct vdpa *vdpa = calloc(1, sizeof(struct vdpa));
+
+ if (!vdpa)
+ return NULL;
+
+ vdpa->indent = alloc_indent_mem();
+ if (!vdpa->indent)
+ goto indent_err;
+
+ return vdpa;
+
+indent_err:
+ free(vdpa);
+ return NULL;
+}
+
+static void vdpa_free(struct vdpa *vdpa)
+{
+ free_indent_mem(vdpa->indent);
+ free(vdpa);
+}
+
+int main(int argc, char **argv)
+{
+ static const struct option long_options[] = {
+ { "Version", no_argument, NULL, 'V' },
+ { "json", no_argument, NULL, 'j' },
+ { "pretty", no_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ struct vdpa *vdpa;
+ int opt;
+ int err;
+ int ret;
+
+ vdpa = vdpa_alloc();
+ if (!vdpa) {
+ fprintf(stderr, "Failed to allocate memory for vdpa\n");
+ return EXIT_FAILURE;
+ }
+
+ while ((opt = getopt_long(argc, argv, "Vjpsh", long_options, NULL)) >= 0) {
+ switch (opt) {
+ case 'V':
+ printf("vdpa utility, iproute2-%s\n", version);
+ ret = EXIT_SUCCESS;
+ goto vdpa_free;
+ case 'j':
+ vdpa->json_output = true;
+ break;
+ case 'p':
+ pretty = true;
+ break;
+ case 'h':
+ help();
+ ret = EXIT_SUCCESS;
+ goto vdpa_free;
+ default:
+ fprintf(stderr, "Unknown option.\n");
+ help();
+ ret = EXIT_FAILURE;
+ goto vdpa_free;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ err = vdpa_init(vdpa);
+ if (err) {
+ ret = EXIT_FAILURE;
+ goto vdpa_free;
+ }
+
+ err = vdpa_cmd(vdpa, argc, argv);
+ if (err) {
+ ret = EXIT_FAILURE;
+ goto vdpa_fini;
+ }
+
+ ret = EXIT_SUCCESS;
+
+vdpa_fini:
+ vdpa_fini(vdpa);
+vdpa_free:
+ vdpa_free(vdpa);
+ return ret;
+}