From: Kishon Vijay Abraham I <kishon@ti.com>
To: Ohad Ben-Cohen <ohad@wizery.com>,
Bjorn Andersson <bjorn.andersson@linaro.org>,
Jon Mason <jdmason@kudzu.us>, Dave Jiang <dave.jiang@intel.com>,
Allen Hubbe <allenbh@gmail.com>,
Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
Bjorn Helgaas <bhelgaas@google.com>,
"Michael S. Tsirkin" <mst@redhat.com>,
Jason Wang <jasowang@redhat.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Stefan Hajnoczi <stefanha@redhat.com>,
Stefano Garzarella <sgarzare@redhat.com>
Cc: <linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-remoteproc@vger.kernel.org>, <linux-ntb@googlegroups.com>,
<linux-pci@vger.kernel.org>, <kvm@vger.kernel.org>,
<virtualization@lists.linux-foundation.org>,
<netdev@vger.kernel.org>
Subject: [RFC PATCH 19/22] PCI: endpoint: Add EP function driver to provide VHOST interface
Date: Thu, 2 Jul 2020 13:51:40 +0530 [thread overview]
Message-ID: <20200702082143.25259-20-kishon@ti.com> (raw)
In-Reply-To: <20200702082143.25259-1-kishon@ti.com>
Add a new endpoint function driver to register VHOST device and
provide interface for the VHOST driver to access virtqueues
created by the remote host (using VIRTIO).
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/endpoint/functions/Kconfig | 11 +
drivers/pci/endpoint/functions/Makefile | 1 +
.../pci/endpoint/functions/pci-epf-vhost.c | 1144 +++++++++++++++++
drivers/vhost/vhost_cfs.c | 13 -
include/linux/vhost.h | 14 +
5 files changed, 1170 insertions(+), 13 deletions(-)
create mode 100644 drivers/pci/endpoint/functions/pci-epf-vhost.c
diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig
index 55ac7bb2d469..21830576e1f4 100644
--- a/drivers/pci/endpoint/functions/Kconfig
+++ b/drivers/pci/endpoint/functions/Kconfig
@@ -24,3 +24,14 @@ config PCI_EPF_NTB
device tree.
If in doubt, say "N" to disable Endpoint NTB driver.
+
+config PCI_EPF_VHOST
+ tristate "PCI Endpoint VHOST driver"
+ depends on PCI_ENDPOINT
+ help
+ Select this configuration option to enable the VHOST driver
+ for PCI Endpoint. EPF VHOST driver implements VIRTIO backend
+ for EPF and uses the VHOST framework to bind any VHOST driver
+ to the VHOST device created by this driver.
+
+ If in doubt, say "N" to disable Endpoint VHOST driver.
diff --git a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile
index 96ab932a537a..39d4f9daf63a 100644
--- a/drivers/pci/endpoint/functions/Makefile
+++ b/drivers/pci/endpoint/functions/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o
obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o
+obj-$(CONFIG_PCI_EPF_VHOST) += pci-epf-vhost.o
diff --git a/drivers/pci/endpoint/functions/pci-epf-vhost.c b/drivers/pci/endpoint/functions/pci-epf-vhost.c
new file mode 100644
index 000000000000..d090e5e88575
--- /dev/null
+++ b/drivers/pci/endpoint/functions/pci-epf-vhost.c
@@ -0,0 +1,1144 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Endpoint Function Driver to implement VHOST functionality
+ *
+ * Copyright (C) 2020 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vhost.h>
+#include <linux/vringh.h>
+
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
+#include <uapi/linux/virtio_pci.h>
+
+#define MAX_VQS 8
+
+#define VHOST_QUEUE_STATUS_ENABLE BIT(0)
+
+#define VHOST_DEVICE_CONFIG_SIZE 1024
+#define EPF_VHOST_MAX_INTERRUPTS (MAX_VQS + 1)
+
+static struct workqueue_struct *kpcivhost_workqueue;
+
+struct epf_vhost_queue {
+ struct delayed_work cmd_handler;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost *vhost;
+ phys_addr_t phys_addr;
+ void __iomem *addr;
+ unsigned int size;
+};
+
+struct epf_vhost {
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost_queue vqueue[MAX_VQS];
+ struct delayed_work cmd_handler;
+ struct delayed_work cfs_work;
+ struct epf_vhost_reg *reg;
+ struct config_group group;
+ size_t msix_table_offset;
+ struct vhost_dev vdev;
+ struct pci_epf *epf;
+ struct vring vring;
+ int msix_bar;
+};
+
+static inline struct epf_vhost *to_epf_vhost_from_ci(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct epf_vhost, group);
+}
+
+#define to_epf_vhost(v) container_of((v), struct epf_vhost, vdev)
+
+struct epf_vhost_reg_queue {
+ u8 cmd;
+ u8 cmd_status;
+ u16 status;
+ u16 num_buffers;
+ u16 msix_vector;
+ u64 queue_addr;
+} __packed;
+
+enum queue_cmd {
+ QUEUE_CMD_NONE,
+ QUEUE_CMD_ACTIVATE,
+ QUEUE_CMD_DEACTIVATE,
+ QUEUE_CMD_NOTIFY,
+};
+
+enum queue_cmd_status {
+ QUEUE_CMD_STATUS_NONE,
+ QUEUE_CMD_STATUS_OKAY,
+ QUEUE_CMD_STATUS_ERROR,
+};
+
+struct epf_vhost_reg {
+ u64 host_features;
+ u64 guest_features;
+ u16 msix_config;
+ u16 num_queues;
+ u8 device_status;
+ u8 config_generation;
+ u32 isr;
+ u8 cmd;
+ u8 cmd_status;
+ struct epf_vhost_reg_queue vq[MAX_VQS];
+} __packed;
+
+enum host_cmd {
+ HOST_CMD_NONE,
+ HOST_CMD_SET_STATUS,
+ HOST_CMD_FINALIZE_FEATURES,
+ HOST_CMD_RESET,
+};
+
+enum host_cmd_status {
+ HOST_CMD_STATUS_NONE,
+ HOST_CMD_STATUS_OKAY,
+ HOST_CMD_STATUS_ERROR,
+};
+
+static struct pci_epf_header epf_vhost_header = {
+ .vendorid = PCI_ANY_ID,
+ .deviceid = PCI_ANY_ID,
+ .baseclass_code = PCI_CLASS_OTHERS,
+ .interrupt_pin = PCI_INTERRUPT_INTA,
+};
+
+/* pci_epf_vhost_cmd_handler - Handle commands from remote EPF virtio driver
+ * @work: The work_struct holding the pci_epf_vhost_cmd_handler() function that
+ * is scheduled
+ *
+ * Handle commands from the remote EPF virtio driver and sends notification to
+ * the vhost client driver. The remote EPF virtio driver sends commands when the
+ * virtio driver status is updated or when the feature negotiation is complete or
+ * if the virtio driver wants to reset the device.
+ */
+static void pci_epf_vhost_cmd_handler(struct work_struct *work)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+ struct device *dev;
+ u8 command;
+
+ vhost = container_of(work, struct epf_vhost, cmd_handler.work);
+ vdev = &vhost->vdev;
+ dev = &vhost->epf->dev;
+ reg = vhost->reg;
+
+ command = reg->cmd;
+ if (!command)
+ goto reset_handler;
+
+ reg->cmd = 0;
+
+ switch (command) {
+ case HOST_CMD_SET_STATUS:
+ blocking_notifier_call_chain(&vdev->notifier, NOTIFY_SET_STATUS,
+ NULL);
+ reg->cmd_status = HOST_CMD_STATUS_OKAY;
+ break;
+ case HOST_CMD_FINALIZE_FEATURES:
+ vdev->features = reg->guest_features;
+ blocking_notifier_call_chain(&vdev->notifier,
+ NOTIFY_FINALIZE_FEATURES, 0);
+ reg->cmd_status = HOST_CMD_STATUS_OKAY;
+ break;
+ case HOST_CMD_RESET:
+ blocking_notifier_call_chain(&vdev->notifier, NOTIFY_RESET, 0);
+ reg->cmd_status = HOST_CMD_STATUS_OKAY;
+ break;
+ default:
+ dev_err(dev, "UNKNOWN command: %d\n", command);
+ break;
+ }
+
+reset_handler:
+ queue_delayed_work(kpcivhost_workqueue, &vhost->cmd_handler,
+ msecs_to_jiffies(1));
+}
+
+/* pci_epf_vhost_queue_activate - Map virtqueue local address to remote
+ * virtqueue address provided by EPF virtio
+ * @vqueue: struct epf_vhost_queue holding the local virtqueue address
+ *
+ * In order for the local system to access the remote virtqueue, the address
+ * reserved in local system should be mapped to the remote virtqueue address.
+ * Map local virtqueue address to remote virtqueue address here.
+ */
+static int pci_epf_vhost_queue_activate(struct epf_vhost_queue *vqueue)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost_reg *reg;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ u64 vq_remote_addr;
+ size_t vq_size;
+ u8 func_no;
+ int ret;
+
+ vhost = vqueue->vhost;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+
+ vq = vqueue->vq;
+ reg = vhost->reg;
+ reg_queue = ®->vq[vq->index];
+ vq_phys_addr = vqueue->phys_addr;
+ vq_remote_addr = reg_queue->queue_addr;
+ vq_size = vqueue->size;
+
+ ret = pci_epc_map_addr(epc, func_no, vq_phys_addr, vq_remote_addr,
+ vq_size);
+ if (ret) {
+ dev_err(dev, "Failed to map outbound address\n");
+ return ret;
+ }
+
+ reg_queue->status |= VHOST_QUEUE_STATUS_ENABLE;
+
+ return 0;
+}
+
+/* pci_epf_vhost_queue_deactivate - Unmap virtqueue local address from remote
+ * virtqueue address
+ * @vqueue: struct epf_vhost_queue holding the local virtqueue address
+ *
+ * Unmap virtqueue local address from remote virtqueue address.
+ */
+static void pci_epf_vhost_queue_deactivate(struct epf_vhost_queue *vqueue)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost_reg *reg;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ u8 func_no;
+
+ vhost = vqueue->vhost;
+
+ epf = vhost->epf;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ vq_phys_addr = vqueue->phys_addr;
+
+ pci_epc_unmap_addr(epc, func_no, vq_phys_addr);
+
+ reg = vhost->reg;
+ vq = vqueue->vq;
+ reg_queue = ®->vq[vq->index];
+ reg_queue->status &= ~VHOST_QUEUE_STATUS_ENABLE;
+}
+
+/* pci_epf_vhost_queue_cmd_handler - Handle commands from remote EPF virtio
+ * driver sent for a particular virtqueue
+ * @work: The work_struct holding the pci_epf_vhost_queue_cmd_handler()
+ * function that is scheduled
+ *
+ * Handle commands from the remote EPF virtio driver sent for a particular
+ * virtqueue to activate/de-activate a virtqueue or to send notification to
+ * the vhost client driver.
+ */
+static void pci_epf_vhost_queue_cmd_handler(struct work_struct *work)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct epf_vhost_queue *vqueue;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+ struct device *dev;
+ u8 command;
+ int ret;
+
+ vqueue = container_of(work, struct epf_vhost_queue, cmd_handler.work);
+ vhost = vqueue->vhost;
+ reg = vhost->reg;
+ vq = vqueue->vq;
+ reg_queue = ®->vq[vq->index];
+ dev = &vhost->epf->dev;
+
+ command = reg_queue->cmd;
+ if (!command)
+ goto reset_handler;
+
+ reg_queue->cmd = 0;
+ vq = vqueue->vq;
+
+ switch (command) {
+ case QUEUE_CMD_ACTIVATE:
+ ret = pci_epf_vhost_queue_activate(vqueue);
+ if (ret)
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_ERROR;
+ else
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_OKAY;
+ break;
+ case QUEUE_CMD_DEACTIVATE:
+ pci_epf_vhost_queue_deactivate(vqueue);
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_OKAY;
+ break;
+ case QUEUE_CMD_NOTIFY:
+ vhost_virtqueue_callback(vqueue->vq);
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_OKAY;
+ break;
+ default:
+ dev_err(dev, "UNKNOWN QUEUE command: %d\n", command);
+ break;
+}
+
+reset_handler:
+ queue_delayed_work(kpcivhost_workqueue, &vqueue->cmd_handler,
+ msecs_to_jiffies(1));
+}
+
+/* pci_epf_vhost_write - Write data to buffer provided by remote virtio driver
+ * @vdev: Vhost device that communicates with remove virtio device
+ * @dst: Buffer address present in the memory of the remote system to which
+ * data should be written
+ * @src: Buffer address in the local device provided by the vhost client driver
+ * @len: Length of the data to be copied from @src to @dst
+ *
+ * Write data to buffer provided by remote virtio driver from buffer provided
+ * by vhost client driver.
+ */
+static int pci_epf_vhost_write(struct vhost_dev *vdev, u64 dst, void *src, int len)
+{
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost *vhost;
+ phys_addr_t phys_addr;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ void __iomem *addr;
+ struct device *dev;
+ int offset, ret;
+ u64 dst_addr;
+ size_t align;
+ u8 func_no;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ epc_features = vhost->epc_features;
+ align = epc_features->align;
+
+ offset = dst & (align - 1);
+ dst_addr = dst & ~(align - 1);
+
+ addr = pci_epc_mem_alloc_addr(epc, &phys_addr, len);
+ if (!addr) {
+ dev_err(dev, "Failed to allocate outbound address\n");
+ return -ENOMEM;
+ }
+
+ ret = pci_epc_map_addr(epc, func_no, phys_addr, dst_addr, len);
+ if (ret) {
+ dev_err(dev, "Failed to map outbound address\n");
+ goto ret;
+ }
+
+ memcpy_toio(addr + offset, src, len);
+
+ pci_epc_unmap_addr(epc, func_no, phys_addr);
+
+ret:
+ pci_epc_mem_free_addr(epc, phys_addr, addr, len);
+
+ return ret;
+}
+
+/* ntb_vhost_read - Read data from buffer provided by remote virtio driver
+ * @vdev: Vhost device that communicates with remove virtio device
+ * @dst: Buffer address in the local device provided by the vhost client driver
+ * @src: Buffer address in the remote device provided by the remote virtio
+ * driver
+ * @len: Length of the data to be copied from @src to @dst
+ *
+ * Read data from buffer provided by remote virtio driver to address provided
+ * by vhost client driver.
+ */
+static int pci_epf_vhost_read(struct vhost_dev *vdev, void *dst, u64 src, int len)
+{
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost *vhost;
+ phys_addr_t phys_addr;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ void __iomem *addr;
+ struct device *dev;
+ int offset, ret;
+ u64 src_addr;
+ size_t align;
+ u8 func_no;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ epc_features = vhost->epc_features;
+ align = epc_features->align;
+
+ offset = src & (align - 1);
+ src_addr = src & ~(align - 1);
+
+ addr = pci_epc_mem_alloc_addr(epc, &phys_addr, len);
+ if (!addr) {
+ dev_err(dev, "Failed to allocate outbound address\n");
+ return -ENOMEM;
+ }
+
+ ret = pci_epc_map_addr(epc, func_no, phys_addr, src_addr, len);
+ if (ret) {
+ dev_err(dev, "Failed to map outbound address\n");
+ goto ret;
+ }
+
+ memcpy_fromio(dst, addr + offset, len);
+
+ pci_epc_unmap_addr(epc, func_no, phys_addr);
+
+ret:
+ pci_epc_mem_free_addr(epc, phys_addr, addr, len);
+
+ return ret;
+}
+
+/* pci_epf_vhost_notify - Send notification to the remote virtqueue
+ * @vq: The local vhost virtqueue corresponding to the remote virtio virtqueue
+ *
+ * Use endpoint core framework to raise MSI-X interrupt to notify the remote
+ * virtqueue.
+ */
+static void pci_epf_vhost_notify(struct vhost_virtqueue *vq)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ u8 func_no;
+
+ vdev = vq->dev;
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ func_no = epf->func_no;
+ epc = epf->epc;
+ reg = vhost->reg;
+ reg_queue = ®->vq[vq->index];
+
+ pci_epc_raise_irq(epc, func_no, PCI_EPC_IRQ_MSIX,
+ reg_queue->msix_vector + 1);
+}
+
+/* pci_epf_vhost_del_vqs - Delete all the vqs associated with the vhost device
+ * @vdev: Vhost device that communicates with remove virtio device
+ *
+ * Delete all the vqs associated with the vhost device and free the memory
+ * address reserved for accessing the remote virtqueue.
+ */
+static void pci_epf_vhost_del_vqs(struct vhost_dev *vdev)
+{
+ struct epf_vhost_queue *vqueue;
+ struct vhost_virtqueue *vq;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ void __iomem *vq_addr;
+ unsigned int vq_size;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ int i;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ epc = epf->epc;
+
+ for (i = 0; i < vdev->nvqs; i++) {
+ vq = vdev->vqs[i];
+ if (IS_ERR_OR_NULL(vq))
+ continue;
+
+ vqueue = &vhost->vqueue[i];
+ vq_phys_addr = vqueue->phys_addr;
+ vq_addr = vqueue->addr;
+ vq_size = vqueue->size;
+ pci_epc_mem_free_addr(epc, vq_phys_addr, vq_addr, vq_size);
+ kfree(vq);
+ }
+}
+
+/* pci_epf_vhost_create_vq - Create a new vhost virtqueue
+ * @vdev: Vhost device that communicates with remove virtio device
+ * @index: Index of the vhost virtqueue
+ * @num_bufs: The number of buffers that should be supported by the vhost
+ * virtqueue (number of descriptors in the vhost virtqueue)
+ * @callback: Callback function associated with the virtqueue
+ *
+ * Create a new vhost virtqueue which can be used by the vhost client driver
+ * to access the remote virtio. This sets up the local address of the vhost
+ * virtqueue but shouldn't be accessed until the virtio sets the status to
+ * VIRTIO_CONFIG_S_DRIVER_OK.
+ */
+static struct vhost_virtqueue *
+pci_epf_vhost_create_vq(struct vhost_dev *vdev, int index,
+ unsigned int num_bufs,
+ void (*callback)(struct vhost_virtqueue *))
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct epf_vhost_queue *vqueue;
+ struct epf_vhost_reg *reg;
+ struct vhost_virtqueue *vq;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ struct vringh *vringh;
+ void __iomem *vq_addr;
+ unsigned int vq_size;
+ struct vring *vring;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ int ret;
+
+ vhost = to_epf_vhost(vdev);
+ vqueue = &vhost->vqueue[index];
+ reg = vhost->reg;
+ reg_queue = ®->vq[index];
+ epf = vhost->epf;
+ epc = epf->epc;
+ dev = &epf->dev;
+
+ vq = kzalloc(sizeof(*vq), GFP_KERNEL);
+ if (!vq)
+ return ERR_PTR(-ENOMEM);
+
+ vq->dev = vdev;
+ vq->callback = callback;
+ vq->num = num_bufs;
+ vq->index = index;
+ vq->notify = pci_epf_vhost_notify;
+ vq->type = VHOST_TYPE_MMIO;
+
+ vqueue->vq = vq;
+ vqueue->vhost = vhost;
+
+ vringh = &vq->vringh;
+ vring = &vringh->vring;
+ reg_queue->num_buffers = num_bufs;
+
+ vq_size = vring_size(num_bufs, VIRTIO_PCI_VRING_ALIGN);
+ vq_addr = pci_epc_mem_alloc_addr(epc, &vq_phys_addr, vq_size);
+ if (!vq_addr) {
+ dev_err(dev, "Failed to allocate virtqueue address\n");
+ ret = -ENOMEM;
+ goto err_mem_alloc_addr;
+ }
+
+ vring_init(vring, num_bufs, vq_addr, VIRTIO_PCI_VRING_ALIGN);
+ ret = vringh_init_mmio(vringh, 0, num_bufs, false, vring->desc,
+ vring->avail, vring->used);
+ if (ret) {
+ dev_err(dev, "Failed to init vringh\n");
+ goto err_init_mmio;
+ }
+
+ vqueue->phys_addr = vq_phys_addr;
+ vqueue->addr = vq_addr;
+ vqueue->size = vq_size;
+
+ INIT_DELAYED_WORK(&vqueue->cmd_handler, pci_epf_vhost_queue_cmd_handler);
+ queue_work(kpcivhost_workqueue, &vqueue->cmd_handler.work);
+
+ return vq;
+
+err_init_mmio:
+ pci_epc_mem_free_addr(epc, vq_phys_addr, vq_addr, vq_size);
+
+err_mem_alloc_addr:
+ kfree(vq);
+
+ return ERR_PTR(ret);
+}
+
+/* pci_epf_vhost_create_vqs - Create vhost virtqueues for vhost device
+ * @vdev: Vhost device that communicates with the remote virtio device
+ * @nvqs: Number of vhost virtqueues to be created
+ * @num_bufs: The number of buffers that should be supported by the vhost
+ * virtqueue (number of descriptors in the vhost virtqueue)
+ * @vqs: Pointers to all the created vhost virtqueues
+ * @callback: Callback function associated with the virtqueue
+ * @names: Names associated with each virtqueue
+ *
+ * Create vhost virtqueues for vhost device. This acts as a wrapper to
+ * pci_epf_vhost_create_vq() which creates individual vhost virtqueue.
+ */
+static int pci_epf_vhost_create_vqs(struct vhost_dev *vdev, unsigned int nvqs,
+ unsigned int num_bufs,
+ struct vhost_virtqueue *vqs[],
+ vhost_vq_callback_t *callbacks[],
+ const char * const names[])
+{
+ struct epf_vhost *vhost;
+ struct pci_epf *epf;
+ struct device *dev;
+ int ret, i;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ dev = &epf->dev;
+
+ for (i = 0; i < nvqs; i++) {
+ vqs[i] = pci_epf_vhost_create_vq(vdev, i, num_bufs,
+ callbacks[i]);
+ if (IS_ERR_OR_NULL(vqs[i])) {
+ ret = PTR_ERR(vqs[i]);
+ dev_err(dev, "Failed to create virtqueue\n");
+ goto err;
+ }
+ }
+
+ vdev->nvqs = nvqs;
+ vdev->vqs = vqs;
+
+ return 0;
+
+err:
+ pci_epf_vhost_del_vqs(vdev);
+ return ret;
+}
+
+/* pci_epf_vhost_set_features - vhost_config_ops to set vhost device features
+ * @vdev: Vhost device that communicates with the remote virtio device
+ * @features: Features supported by the vhost client driver
+ *
+ * vhost_config_ops invoked by the vhost client driver to set vhost device
+ * features.
+ */
+static int pci_epf_vhost_set_features(struct vhost_dev *vdev, u64 features)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+
+ vhost = to_epf_vhost(vdev);
+ reg = vhost->reg;
+
+ reg->host_features = features;
+
+ return 0;
+}
+
+/* ntb_vhost_set_status - vhost_config_ops to set vhost device status
+ * @vdev: Vhost device that communicates with the remote virtio device
+ * @status: Vhost device status configured by vhost client driver
+ *
+ * vhost_config_ops invoked by the vhost client driver to set vhost device
+ * status.
+ */
+static int pci_epf_vhost_set_status(struct vhost_dev *vdev, u8 status)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+
+ vhost = to_epf_vhost(vdev);
+ reg = vhost->reg;
+
+ reg->device_status = status;
+
+ return 0;
+}
+
+/* ntb_vhost_get_status - vhost_config_ops to get vhost device status
+ * @vdev: Vhost device that communicates with the remote virtio device
+ *
+ * vhost_config_ops invoked by the vhost client driver to get vhost device
+ * status set by the remote virtio driver.
+ */
+static u8 pci_epf_vhost_get_status(struct vhost_dev *vdev)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+
+ vhost = to_epf_vhost(vdev);
+ reg = vhost->reg;
+
+ return reg->device_status;
+}
+
+static const struct vhost_config_ops pci_epf_vhost_ops = {
+ .create_vqs = pci_epf_vhost_create_vqs,
+ .del_vqs = pci_epf_vhost_del_vqs,
+ .write = pci_epf_vhost_write,
+ .read = pci_epf_vhost_read,
+ .set_features = pci_epf_vhost_set_features,
+ .set_status = pci_epf_vhost_set_status,
+ .get_status = pci_epf_vhost_get_status,
+};
+
+/* pci_epf_vhost_write_header - Write to PCIe standard configuration space
+ * header
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Invokes endpoint core framework's pci_epc_write_header() to write to the
+ * standard configuration space header.
+ */
+static int pci_epf_vhost_write_header(struct epf_vhost *vhost)
+{
+ struct pci_epf_header *header;
+ struct vhost_dev *vdev;
+ struct pci_epc *epc;
+ struct pci_epf *epf;
+ struct device *dev;
+ u8 func_no;
+ int ret;
+
+ vdev = &vhost->vdev;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ header = epf->header;
+
+ ret = pci_epc_write_header(epc, func_no, header);
+ if (ret) {
+ dev_err(dev, "Configuration header write failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* pci_epf_vhost_release_dev - Callback function to free device
+ * @dev: Device in vhost_dev that has to be freed
+ *
+ * Callback function from device core invoked to free the device after
+ * all references have been removed. This frees the allocated memory for
+ * struct ntb_vhost.
+ */
+static void pci_epf_vhost_release_dev(struct device *dev)
+{
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+
+ vdev = to_vhost_dev(dev);
+ vhost = to_epf_vhost(vdev);
+
+ kfree(vhost);
+}
+
+/* pci_epf_vhost_register - Register a vhost device
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Invoked vhost_register_device() to register a vhost device after populating
+ * the deviceID and vendorID of the vhost device.
+ */
+static int pci_epf_vhost_register(struct epf_vhost *vhost)
+{
+ struct vhost_dev *vdev;
+ struct pci_epf *epf;
+ struct device *dev;
+ int ret;
+
+ vdev = &vhost->vdev;
+ epf = vhost->epf;
+ dev = &epf->dev;
+
+ vdev->dev.parent = dev;
+ vdev->dev.release = pci_epf_vhost_release_dev;
+ vdev->id.device = vhost->epf->header->subsys_id;
+ vdev->id.vendor = vhost->epf->header->subsys_vendor_id;
+ vdev->ops = &pci_epf_vhost_ops;
+
+ ret = vhost_register_device(vdev);
+ if (ret) {
+ dev_err(dev, "Failed to register vhost device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* pci_epf_vhost_configure_bar - Configure BAR of EPF device
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Allocate memory for the standard virtio configuration space and map it to
+ * the first free BAR.
+ */
+static int pci_epf_vhost_configure_bar(struct epf_vhost *vhost)
+{
+ size_t msix_table_size = 0, pba_size = 0, align, bar_size;
+ const struct pci_epc_features *epc_features;
+ struct pci_epf_bar *epf_bar;
+ struct vhost_dev *vdev;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ bool msix_capable;
+ u32 config_size;
+ int barno, ret;
+ void *base;
+ u64 size;
+
+ vdev = &vhost->vdev;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+
+ epc_features = vhost->epc_features;
+ barno = pci_epc_get_first_free_bar(epc_features);
+ if (barno < 0) {
+ dev_err(dev, "Failed to get free BAR\n");
+ return barno;
+ }
+
+ size = epc_features->bar_fixed_size[barno];
+ align = epc_features->align;
+ /* Check if epc_features is populated incorrectly */
+ if ((!IS_ALIGNED(size, align)))
+ return -EINVAL;
+
+ config_size = sizeof(struct epf_vhost_reg) + VHOST_DEVICE_CONFIG_SIZE;
+ config_size = ALIGN(config_size, 8);
+
+ msix_capable = epc_features->msix_capable;
+ if (msix_capable) {
+ msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
+ vhost->msix_table_offset = config_size;
+ vhost->msix_bar = barno;
+ /* Align to QWORD or 8 Bytes */
+ pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8);
+ }
+
+ bar_size = config_size + msix_table_size + pba_size;
+
+ if (!align)
+ bar_size = roundup_pow_of_two(bar_size);
+ else
+ bar_size = ALIGN(bar_size, align);
+
+ if (!size)
+ size = bar_size;
+ else if (size < bar_size)
+ return -EINVAL;
+
+ base = pci_epf_alloc_space(epf, size, barno, align,
+ PRIMARY_INTERFACE);
+ if (!base) {
+ dev_err(dev, "Failed to allocate configuration region\n");
+ return -ENOMEM;
+ }
+
+ epf_bar = &epf->bar[barno];
+ ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
+ if (ret) {
+ dev_err(dev, "Failed to set BAR: %d\n", barno);
+ goto err_set_bar;
+ }
+
+ vhost->reg = base;
+
+ return 0;
+
+err_set_bar:
+ pci_epf_free_space(epf, base, barno, PRIMARY_INTERFACE);
+
+ return ret;
+}
+
+/* pci_epf_vhost_configure_interrupts - Configure MSI/MSI-X capability of EPF
+ * device
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Configure MSI/MSI-X capability of EPF device. This will be used to interrupt
+ * the vhost virtqueue.
+ */
+static int pci_epf_vhost_configure_interrupts(struct epf_vhost *vhost)
+{
+ const struct pci_epc_features *epc_features;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ int ret;
+
+ epc_features = vhost->epc_features;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+
+ if (epc_features->msi_capable) {
+ ret = pci_epc_set_msi(epc, epf->func_no,
+ EPF_VHOST_MAX_INTERRUPTS);
+ if (ret) {
+ dev_err(dev, "MSI configuration failed\n");
+ return ret;
+ }
+ }
+
+ if (epc_features->msix_capable) {
+ ret = pci_epc_set_msix(epc, epf->func_no,
+ EPF_VHOST_MAX_INTERRUPTS,
+ vhost->msix_bar,
+ vhost->msix_table_offset);
+ if (ret) {
+ dev_err(dev, "MSI-X configuration failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* pci_epf_vhost_cfs_link - Link vhost client driver with EPF vhost to get
+ * the deviceID and driverID to be writtent to the PCIe config space
+ * @epf_vhost_item: Config item representing the EPF vhost created by this
+ * driver
+ * @epf: Endpoint function device that is bound to the endpoint controller
+ *
+ * This is invoked when the user creates a softlink between the vhost client
+ * to the EPF vhost. This gets the deviceID and vendorID data from the vhost
+ * client and copies it to the subys_id and subsys_vendor_id of the EPF
+ * header. This will be used by the remote virtio to bind a virtio client
+ * driver.
+ */
+static int pci_epf_vhost_cfs_link(struct config_item *epf_vhost_item,
+ struct config_item *driver_item)
+{
+ struct vhost_driver_item *vdriver_item;
+ struct epf_vhost *vhost;
+
+ vdriver_item = to_vhost_driver_item(driver_item);
+ vhost = to_epf_vhost_from_ci(epf_vhost_item);
+
+ vhost->epf->header->subsys_id = vdriver_item->device;
+ vhost->epf->header->subsys_vendor_id = vdriver_item->vendor;
+
+ return 0;
+}
+
+static struct configfs_item_operations pci_epf_vhost_cfs_ops = {
+ .allow_link = pci_epf_vhost_cfs_link,
+};
+
+static const struct config_item_type pci_epf_vhost_cfs_type = {
+ .ct_item_ops = &pci_epf_vhost_cfs_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* pci_epf_vhost_cfs_work - Delayed work function to create configfs directory
+ * to perform EPF vhost specific initializations
+ * @work: The work_struct holding the pci_epf_vhost_cfs_work() function that
+ * is scheduled
+ *
+ * This is a delayed work function to create configfs directory to perform EPF
+ * vhost specific initializations. This configfs directory will be a
+ * sub-directory to the directory created by the user to create pci_epf device.
+ */
+static void pci_epf_vhost_cfs_work(struct work_struct *work)
+{
+ struct epf_vhost *vhost = container_of(work, struct epf_vhost,
+ cfs_work.work);
+ struct pci_epf *epf = vhost->epf;
+ struct device *dev = &epf->dev;
+ struct config_group *group;
+ struct vhost_dev *vdev;
+ int ret;
+
+ if (!epf->group) {
+ queue_delayed_work(kpcivhost_workqueue, &vhost->cfs_work,
+ msecs_to_jiffies(50));
+ return;
+ }
+
+ vdev = &vhost->vdev;
+ group = &vhost->group;
+ config_group_init_type_name(group, dev_name(dev), &pci_epf_vhost_cfs_type);
+ ret = configfs_register_group(epf->group, group);
+ if (ret) {
+ dev_err(dev, "Failed to register configfs group %s\n", dev_name(dev));
+ return;
+ }
+}
+
+/* pci_epf_vhost_probe - Initialize struct epf_vhost when a new EPF device is
+ * created
+ * @epf: Endpoint function device that is bound to this driver
+ *
+ * Probe function to initialize struct epf_vhost when a new EPF device is
+ * created.
+ */
+static int pci_epf_vhost_probe(struct pci_epf *epf)
+{
+ struct epf_vhost *vhost;
+
+ vhost = kzalloc(sizeof(*vhost), GFP_KERNEL);
+ if (!vhost)
+ return -ENOMEM;
+
+ epf->header = &epf_vhost_header;
+ vhost->epf = epf;
+
+ epf_set_drvdata(epf, vhost);
+ INIT_DELAYED_WORK(&vhost->cmd_handler, pci_epf_vhost_cmd_handler);
+ INIT_DELAYED_WORK(&vhost->cfs_work, pci_epf_vhost_cfs_work);
+ queue_delayed_work(kpcivhost_workqueue, &vhost->cfs_work,
+ msecs_to_jiffies(50));
+
+ return 0;
+}
+
+/* pci_epf_vhost_remove - Free the initializations performed by
+ * pci_epf_vhost_probe()
+ * @epf: Endpoint function device that is bound to this driver
+ *
+ * Free the initializations performed by pci_epf_vhost_probe().
+ */
+static int pci_epf_vhost_remove(struct pci_epf *epf)
+{
+ struct epf_vhost *vhost;
+
+ vhost = epf_get_drvdata(epf);
+ cancel_delayed_work_sync(&vhost->cfs_work);
+
+ return 0;
+}
+
+/* pci_epf_vhost_bind - Bind callback to initialize the PCIe EP controller
+ * @epf: Endpoint function device that is bound to the endpoint controller
+ *
+ * pci_epf_vhost_bind() is invoked when an endpoint controller is bound to
+ * endpoint function. This function initializes the endpoint controller
+ * with vhost endpoint function specific data.
+ */
+static int pci_epf_vhost_bind(struct pci_epf *epf)
+{
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost *vhost;
+ struct pci_epc *epc;
+ struct device *dev;
+ int ret;
+
+ vhost = epf_get_drvdata(epf);
+ dev = &epf->dev;
+ epc = epf->epc;
+
+ epc_features = pci_epc_get_features(epc, epf->func_no);
+ if (!epc_features) {
+ dev_err(dev, "Fail to get EPC features\n");
+ return -EINVAL;
+ }
+ vhost->epc_features = epc_features;
+
+ ret = pci_epf_vhost_write_header(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to bind VHOST config header\n");
+ return ret;
+ }
+
+ ret = pci_epf_vhost_configure_bar(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to configure BAR\n");
+ return ret;
+ }
+
+ ret = pci_epf_vhost_configure_interrupts(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to configure BAR\n");
+ return ret;
+ }
+
+ ret = pci_epf_vhost_register(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to bind VHOST config header\n");
+ return ret;
+ }
+
+ queue_work(kpcivhost_workqueue, &vhost->cmd_handler.work);
+
+ return ret;
+}
+
+/* pci_epf_vhost_unbind - Inbind callback to cleanup the PCIe EP controller
+ * @epf: Endpoint function device that is bound to the endpoint controller
+ *
+ * pci_epf_vhost_unbind() is invoked when the binding between endpoint
+ * controller is removed from endpoint function. This will unregister vhost
+ * device and cancel pending cmd_handler work.
+ */
+static void pci_epf_vhost_unbind(struct pci_epf *epf)
+{
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+
+ vhost = epf_get_drvdata(epf);
+ vdev = &vhost->vdev;
+
+ cancel_delayed_work_sync(&vhost->cmd_handler);
+ if (device_is_registered(&vdev->dev))
+ vhost_unregister_device(vdev);
+}
+
+static struct pci_epf_ops epf_ops = {
+ .bind = pci_epf_vhost_bind,
+ .unbind = pci_epf_vhost_unbind,
+};
+
+static const struct pci_epf_device_id pci_epf_vhost_ids[] = {
+ {
+ .name = "pci-epf-vhost",
+ },
+ { },
+};
+
+static struct pci_epf_driver epf_vhost_driver = {
+ .driver.name = "pci_epf_vhost",
+ .probe = pci_epf_vhost_probe,
+ .remove = pci_epf_vhost_remove,
+ .id_table = pci_epf_vhost_ids,
+ .ops = &epf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int __init pci_epf_vhost_init(void)
+{
+ int ret;
+
+ kpcivhost_workqueue = alloc_workqueue("kpcivhost", WQ_MEM_RECLAIM |
+ WQ_HIGHPRI, 0);
+ ret = pci_epf_register_driver(&epf_vhost_driver);
+ if (ret) {
+ pr_err("Failed to register pci epf vhost driver --> %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+module_init(pci_epf_vhost_init);
+
+static void __exit pci_epf_vhost_exit(void)
+{
+ pci_epf_unregister_driver(&epf_vhost_driver);
+}
+module_exit(pci_epf_vhost_exit);
+
+MODULE_DESCRIPTION("PCI EPF VHOST DRIVER");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/vhost/vhost_cfs.c b/drivers/vhost/vhost_cfs.c
index ae46e71968f1..ab0393289200 100644
--- a/drivers/vhost/vhost_cfs.c
+++ b/drivers/vhost/vhost_cfs.c
@@ -18,12 +18,6 @@ static struct config_group *vhost_driver_group;
/* VHOST device like PCIe EP, NTB etc., */
static struct config_group *vhost_device_group;
-struct vhost_driver_item {
- struct config_group group;
- u32 vendor;
- u32 device;
-};
-
struct vhost_driver_group {
struct config_group group;
};
@@ -33,13 +27,6 @@ struct vhost_device_item {
struct vhost_dev *vdev;
};
-static inline
-struct vhost_driver_item *to_vhost_driver_item(struct config_item *item)
-{
- return container_of(to_config_group(item), struct vhost_driver_item,
- group);
-}
-
static inline
struct vhost_device_item *to_vhost_device_item(struct config_item *item)
{
diff --git a/include/linux/vhost.h b/include/linux/vhost.h
index be9341ffd266..640650311310 100644
--- a/include/linux/vhost.h
+++ b/include/linux/vhost.h
@@ -74,6 +74,7 @@ struct vhost_virtqueue {
struct vhost_dev *dev;
enum vhost_type type;
struct vringh vringh;
+ int index;
void (*callback)(struct vhost_virtqueue *vq);
void (*notify)(struct vhost_virtqueue *vq);
@@ -148,6 +149,12 @@ struct vhost_msg_node {
struct list_head node;
};
+struct vhost_driver_item {
+ struct config_group group;
+ u32 vendor;
+ u32 device;
+};
+
enum vhost_notify_event {
NOTIFY_SET_STATUS,
NOTIFY_FINALIZE_FEATURES,
@@ -230,6 +237,13 @@ static inline void *vhost_get_drvdata(struct vhost_dev *vdev)
return dev_get_drvdata(&vdev->dev);
}
+static inline
+struct vhost_driver_item *to_vhost_driver_item(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct vhost_driver_item,
+ group);
+}
+
int vhost_register_driver(struct vhost_driver *driver);
void vhost_unregister_driver(struct vhost_driver *driver);
int vhost_register_device(struct vhost_dev *vdev);
--
2.17.1
WARNING: multiple messages have this Message-ID (diff)
From: Kishon Vijay Abraham I <kishon@ti.com>
To: Ohad Ben-Cohen <ohad@wizery.com>,
Bjorn Andersson <bjorn.andersson@linaro.org>,
Jon Mason <jdmason@kudzu.us>, Dave Jiang <dave.jiang@intel.com>,
Allen Hubbe <allenbh@gmail.com>,
Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
Bjorn Helgaas <bhelgaas@google.com>,
"Michael S. Tsirkin" <mst@redhat.com>,
Jason Wang <jasowang@redhat.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Stefan Hajnoczi <stefanha@redhat.com>,
Stefano Garzarella <sgarzare@redhat.com>
Cc: linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-remoteproc@vger.kernel.org, linux-ntb@googlegroups.com,
linux-pci@vger.kernel.org, kvm@vger.kernel.org,
virtualization@lists.linux-foundation.org,
netdev@vger.kernel.org
Subject: [RFC PATCH 19/22] PCI: endpoint: Add EP function driver to provide VHOST interface
Date: Thu, 2 Jul 2020 13:51:40 +0530 [thread overview]
Message-ID: <20200702082143.25259-20-kishon@ti.com> (raw)
In-Reply-To: <20200702082143.25259-1-kishon@ti.com>
Add a new endpoint function driver to register VHOST device and
provide interface for the VHOST driver to access virtqueues
created by the remote host (using VIRTIO).
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/endpoint/functions/Kconfig | 11 +
drivers/pci/endpoint/functions/Makefile | 1 +
.../pci/endpoint/functions/pci-epf-vhost.c | 1144 +++++++++++++++++
drivers/vhost/vhost_cfs.c | 13 -
include/linux/vhost.h | 14 +
5 files changed, 1170 insertions(+), 13 deletions(-)
create mode 100644 drivers/pci/endpoint/functions/pci-epf-vhost.c
diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig
index 55ac7bb2d469..21830576e1f4 100644
--- a/drivers/pci/endpoint/functions/Kconfig
+++ b/drivers/pci/endpoint/functions/Kconfig
@@ -24,3 +24,14 @@ config PCI_EPF_NTB
device tree.
If in doubt, say "N" to disable Endpoint NTB driver.
+
+config PCI_EPF_VHOST
+ tristate "PCI Endpoint VHOST driver"
+ depends on PCI_ENDPOINT
+ help
+ Select this configuration option to enable the VHOST driver
+ for PCI Endpoint. EPF VHOST driver implements VIRTIO backend
+ for EPF and uses the VHOST framework to bind any VHOST driver
+ to the VHOST device created by this driver.
+
+ If in doubt, say "N" to disable Endpoint VHOST driver.
diff --git a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile
index 96ab932a537a..39d4f9daf63a 100644
--- a/drivers/pci/endpoint/functions/Makefile
+++ b/drivers/pci/endpoint/functions/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o
obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o
+obj-$(CONFIG_PCI_EPF_VHOST) += pci-epf-vhost.o
diff --git a/drivers/pci/endpoint/functions/pci-epf-vhost.c b/drivers/pci/endpoint/functions/pci-epf-vhost.c
new file mode 100644
index 000000000000..d090e5e88575
--- /dev/null
+++ b/drivers/pci/endpoint/functions/pci-epf-vhost.c
@@ -0,0 +1,1144 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Endpoint Function Driver to implement VHOST functionality
+ *
+ * Copyright (C) 2020 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vhost.h>
+#include <linux/vringh.h>
+
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
+#include <uapi/linux/virtio_pci.h>
+
+#define MAX_VQS 8
+
+#define VHOST_QUEUE_STATUS_ENABLE BIT(0)
+
+#define VHOST_DEVICE_CONFIG_SIZE 1024
+#define EPF_VHOST_MAX_INTERRUPTS (MAX_VQS + 1)
+
+static struct workqueue_struct *kpcivhost_workqueue;
+
+struct epf_vhost_queue {
+ struct delayed_work cmd_handler;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost *vhost;
+ phys_addr_t phys_addr;
+ void __iomem *addr;
+ unsigned int size;
+};
+
+struct epf_vhost {
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost_queue vqueue[MAX_VQS];
+ struct delayed_work cmd_handler;
+ struct delayed_work cfs_work;
+ struct epf_vhost_reg *reg;
+ struct config_group group;
+ size_t msix_table_offset;
+ struct vhost_dev vdev;
+ struct pci_epf *epf;
+ struct vring vring;
+ int msix_bar;
+};
+
+static inline struct epf_vhost *to_epf_vhost_from_ci(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct epf_vhost, group);
+}
+
+#define to_epf_vhost(v) container_of((v), struct epf_vhost, vdev)
+
+struct epf_vhost_reg_queue {
+ u8 cmd;
+ u8 cmd_status;
+ u16 status;
+ u16 num_buffers;
+ u16 msix_vector;
+ u64 queue_addr;
+} __packed;
+
+enum queue_cmd {
+ QUEUE_CMD_NONE,
+ QUEUE_CMD_ACTIVATE,
+ QUEUE_CMD_DEACTIVATE,
+ QUEUE_CMD_NOTIFY,
+};
+
+enum queue_cmd_status {
+ QUEUE_CMD_STATUS_NONE,
+ QUEUE_CMD_STATUS_OKAY,
+ QUEUE_CMD_STATUS_ERROR,
+};
+
+struct epf_vhost_reg {
+ u64 host_features;
+ u64 guest_features;
+ u16 msix_config;
+ u16 num_queues;
+ u8 device_status;
+ u8 config_generation;
+ u32 isr;
+ u8 cmd;
+ u8 cmd_status;
+ struct epf_vhost_reg_queue vq[MAX_VQS];
+} __packed;
+
+enum host_cmd {
+ HOST_CMD_NONE,
+ HOST_CMD_SET_STATUS,
+ HOST_CMD_FINALIZE_FEATURES,
+ HOST_CMD_RESET,
+};
+
+enum host_cmd_status {
+ HOST_CMD_STATUS_NONE,
+ HOST_CMD_STATUS_OKAY,
+ HOST_CMD_STATUS_ERROR,
+};
+
+static struct pci_epf_header epf_vhost_header = {
+ .vendorid = PCI_ANY_ID,
+ .deviceid = PCI_ANY_ID,
+ .baseclass_code = PCI_CLASS_OTHERS,
+ .interrupt_pin = PCI_INTERRUPT_INTA,
+};
+
+/* pci_epf_vhost_cmd_handler - Handle commands from remote EPF virtio driver
+ * @work: The work_struct holding the pci_epf_vhost_cmd_handler() function that
+ * is scheduled
+ *
+ * Handle commands from the remote EPF virtio driver and sends notification to
+ * the vhost client driver. The remote EPF virtio driver sends commands when the
+ * virtio driver status is updated or when the feature negotiation is complete or
+ * if the virtio driver wants to reset the device.
+ */
+static void pci_epf_vhost_cmd_handler(struct work_struct *work)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+ struct device *dev;
+ u8 command;
+
+ vhost = container_of(work, struct epf_vhost, cmd_handler.work);
+ vdev = &vhost->vdev;
+ dev = &vhost->epf->dev;
+ reg = vhost->reg;
+
+ command = reg->cmd;
+ if (!command)
+ goto reset_handler;
+
+ reg->cmd = 0;
+
+ switch (command) {
+ case HOST_CMD_SET_STATUS:
+ blocking_notifier_call_chain(&vdev->notifier, NOTIFY_SET_STATUS,
+ NULL);
+ reg->cmd_status = HOST_CMD_STATUS_OKAY;
+ break;
+ case HOST_CMD_FINALIZE_FEATURES:
+ vdev->features = reg->guest_features;
+ blocking_notifier_call_chain(&vdev->notifier,
+ NOTIFY_FINALIZE_FEATURES, 0);
+ reg->cmd_status = HOST_CMD_STATUS_OKAY;
+ break;
+ case HOST_CMD_RESET:
+ blocking_notifier_call_chain(&vdev->notifier, NOTIFY_RESET, 0);
+ reg->cmd_status = HOST_CMD_STATUS_OKAY;
+ break;
+ default:
+ dev_err(dev, "UNKNOWN command: %d\n", command);
+ break;
+ }
+
+reset_handler:
+ queue_delayed_work(kpcivhost_workqueue, &vhost->cmd_handler,
+ msecs_to_jiffies(1));
+}
+
+/* pci_epf_vhost_queue_activate - Map virtqueue local address to remote
+ * virtqueue address provided by EPF virtio
+ * @vqueue: struct epf_vhost_queue holding the local virtqueue address
+ *
+ * In order for the local system to access the remote virtqueue, the address
+ * reserved in local system should be mapped to the remote virtqueue address.
+ * Map local virtqueue address to remote virtqueue address here.
+ */
+static int pci_epf_vhost_queue_activate(struct epf_vhost_queue *vqueue)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost_reg *reg;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ u64 vq_remote_addr;
+ size_t vq_size;
+ u8 func_no;
+ int ret;
+
+ vhost = vqueue->vhost;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+
+ vq = vqueue->vq;
+ reg = vhost->reg;
+ reg_queue = ®->vq[vq->index];
+ vq_phys_addr = vqueue->phys_addr;
+ vq_remote_addr = reg_queue->queue_addr;
+ vq_size = vqueue->size;
+
+ ret = pci_epc_map_addr(epc, func_no, vq_phys_addr, vq_remote_addr,
+ vq_size);
+ if (ret) {
+ dev_err(dev, "Failed to map outbound address\n");
+ return ret;
+ }
+
+ reg_queue->status |= VHOST_QUEUE_STATUS_ENABLE;
+
+ return 0;
+}
+
+/* pci_epf_vhost_queue_deactivate - Unmap virtqueue local address from remote
+ * virtqueue address
+ * @vqueue: struct epf_vhost_queue holding the local virtqueue address
+ *
+ * Unmap virtqueue local address from remote virtqueue address.
+ */
+static void pci_epf_vhost_queue_deactivate(struct epf_vhost_queue *vqueue)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost_reg *reg;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ u8 func_no;
+
+ vhost = vqueue->vhost;
+
+ epf = vhost->epf;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ vq_phys_addr = vqueue->phys_addr;
+
+ pci_epc_unmap_addr(epc, func_no, vq_phys_addr);
+
+ reg = vhost->reg;
+ vq = vqueue->vq;
+ reg_queue = ®->vq[vq->index];
+ reg_queue->status &= ~VHOST_QUEUE_STATUS_ENABLE;
+}
+
+/* pci_epf_vhost_queue_cmd_handler - Handle commands from remote EPF virtio
+ * driver sent for a particular virtqueue
+ * @work: The work_struct holding the pci_epf_vhost_queue_cmd_handler()
+ * function that is scheduled
+ *
+ * Handle commands from the remote EPF virtio driver sent for a particular
+ * virtqueue to activate/de-activate a virtqueue or to send notification to
+ * the vhost client driver.
+ */
+static void pci_epf_vhost_queue_cmd_handler(struct work_struct *work)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct epf_vhost_queue *vqueue;
+ struct vhost_virtqueue *vq;
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+ struct device *dev;
+ u8 command;
+ int ret;
+
+ vqueue = container_of(work, struct epf_vhost_queue, cmd_handler.work);
+ vhost = vqueue->vhost;
+ reg = vhost->reg;
+ vq = vqueue->vq;
+ reg_queue = ®->vq[vq->index];
+ dev = &vhost->epf->dev;
+
+ command = reg_queue->cmd;
+ if (!command)
+ goto reset_handler;
+
+ reg_queue->cmd = 0;
+ vq = vqueue->vq;
+
+ switch (command) {
+ case QUEUE_CMD_ACTIVATE:
+ ret = pci_epf_vhost_queue_activate(vqueue);
+ if (ret)
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_ERROR;
+ else
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_OKAY;
+ break;
+ case QUEUE_CMD_DEACTIVATE:
+ pci_epf_vhost_queue_deactivate(vqueue);
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_OKAY;
+ break;
+ case QUEUE_CMD_NOTIFY:
+ vhost_virtqueue_callback(vqueue->vq);
+ reg_queue->cmd_status = QUEUE_CMD_STATUS_OKAY;
+ break;
+ default:
+ dev_err(dev, "UNKNOWN QUEUE command: %d\n", command);
+ break;
+}
+
+reset_handler:
+ queue_delayed_work(kpcivhost_workqueue, &vqueue->cmd_handler,
+ msecs_to_jiffies(1));
+}
+
+/* pci_epf_vhost_write - Write data to buffer provided by remote virtio driver
+ * @vdev: Vhost device that communicates with remove virtio device
+ * @dst: Buffer address present in the memory of the remote system to which
+ * data should be written
+ * @src: Buffer address in the local device provided by the vhost client driver
+ * @len: Length of the data to be copied from @src to @dst
+ *
+ * Write data to buffer provided by remote virtio driver from buffer provided
+ * by vhost client driver.
+ */
+static int pci_epf_vhost_write(struct vhost_dev *vdev, u64 dst, void *src, int len)
+{
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost *vhost;
+ phys_addr_t phys_addr;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ void __iomem *addr;
+ struct device *dev;
+ int offset, ret;
+ u64 dst_addr;
+ size_t align;
+ u8 func_no;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ epc_features = vhost->epc_features;
+ align = epc_features->align;
+
+ offset = dst & (align - 1);
+ dst_addr = dst & ~(align - 1);
+
+ addr = pci_epc_mem_alloc_addr(epc, &phys_addr, len);
+ if (!addr) {
+ dev_err(dev, "Failed to allocate outbound address\n");
+ return -ENOMEM;
+ }
+
+ ret = pci_epc_map_addr(epc, func_no, phys_addr, dst_addr, len);
+ if (ret) {
+ dev_err(dev, "Failed to map outbound address\n");
+ goto ret;
+ }
+
+ memcpy_toio(addr + offset, src, len);
+
+ pci_epc_unmap_addr(epc, func_no, phys_addr);
+
+ret:
+ pci_epc_mem_free_addr(epc, phys_addr, addr, len);
+
+ return ret;
+}
+
+/* ntb_vhost_read - Read data from buffer provided by remote virtio driver
+ * @vdev: Vhost device that communicates with remove virtio device
+ * @dst: Buffer address in the local device provided by the vhost client driver
+ * @src: Buffer address in the remote device provided by the remote virtio
+ * driver
+ * @len: Length of the data to be copied from @src to @dst
+ *
+ * Read data from buffer provided by remote virtio driver to address provided
+ * by vhost client driver.
+ */
+static int pci_epf_vhost_read(struct vhost_dev *vdev, void *dst, u64 src, int len)
+{
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost *vhost;
+ phys_addr_t phys_addr;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ void __iomem *addr;
+ struct device *dev;
+ int offset, ret;
+ u64 src_addr;
+ size_t align;
+ u8 func_no;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ epc_features = vhost->epc_features;
+ align = epc_features->align;
+
+ offset = src & (align - 1);
+ src_addr = src & ~(align - 1);
+
+ addr = pci_epc_mem_alloc_addr(epc, &phys_addr, len);
+ if (!addr) {
+ dev_err(dev, "Failed to allocate outbound address\n");
+ return -ENOMEM;
+ }
+
+ ret = pci_epc_map_addr(epc, func_no, phys_addr, src_addr, len);
+ if (ret) {
+ dev_err(dev, "Failed to map outbound address\n");
+ goto ret;
+ }
+
+ memcpy_fromio(dst, addr + offset, len);
+
+ pci_epc_unmap_addr(epc, func_no, phys_addr);
+
+ret:
+ pci_epc_mem_free_addr(epc, phys_addr, addr, len);
+
+ return ret;
+}
+
+/* pci_epf_vhost_notify - Send notification to the remote virtqueue
+ * @vq: The local vhost virtqueue corresponding to the remote virtio virtqueue
+ *
+ * Use endpoint core framework to raise MSI-X interrupt to notify the remote
+ * virtqueue.
+ */
+static void pci_epf_vhost_notify(struct vhost_virtqueue *vq)
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ u8 func_no;
+
+ vdev = vq->dev;
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ func_no = epf->func_no;
+ epc = epf->epc;
+ reg = vhost->reg;
+ reg_queue = ®->vq[vq->index];
+
+ pci_epc_raise_irq(epc, func_no, PCI_EPC_IRQ_MSIX,
+ reg_queue->msix_vector + 1);
+}
+
+/* pci_epf_vhost_del_vqs - Delete all the vqs associated with the vhost device
+ * @vdev: Vhost device that communicates with remove virtio device
+ *
+ * Delete all the vqs associated with the vhost device and free the memory
+ * address reserved for accessing the remote virtqueue.
+ */
+static void pci_epf_vhost_del_vqs(struct vhost_dev *vdev)
+{
+ struct epf_vhost_queue *vqueue;
+ struct vhost_virtqueue *vq;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ void __iomem *vq_addr;
+ unsigned int vq_size;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ int i;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ epc = epf->epc;
+
+ for (i = 0; i < vdev->nvqs; i++) {
+ vq = vdev->vqs[i];
+ if (IS_ERR_OR_NULL(vq))
+ continue;
+
+ vqueue = &vhost->vqueue[i];
+ vq_phys_addr = vqueue->phys_addr;
+ vq_addr = vqueue->addr;
+ vq_size = vqueue->size;
+ pci_epc_mem_free_addr(epc, vq_phys_addr, vq_addr, vq_size);
+ kfree(vq);
+ }
+}
+
+/* pci_epf_vhost_create_vq - Create a new vhost virtqueue
+ * @vdev: Vhost device that communicates with remove virtio device
+ * @index: Index of the vhost virtqueue
+ * @num_bufs: The number of buffers that should be supported by the vhost
+ * virtqueue (number of descriptors in the vhost virtqueue)
+ * @callback: Callback function associated with the virtqueue
+ *
+ * Create a new vhost virtqueue which can be used by the vhost client driver
+ * to access the remote virtio. This sets up the local address of the vhost
+ * virtqueue but shouldn't be accessed until the virtio sets the status to
+ * VIRTIO_CONFIG_S_DRIVER_OK.
+ */
+static struct vhost_virtqueue *
+pci_epf_vhost_create_vq(struct vhost_dev *vdev, int index,
+ unsigned int num_bufs,
+ void (*callback)(struct vhost_virtqueue *))
+{
+ struct epf_vhost_reg_queue *reg_queue;
+ struct epf_vhost_queue *vqueue;
+ struct epf_vhost_reg *reg;
+ struct vhost_virtqueue *vq;
+ phys_addr_t vq_phys_addr;
+ struct epf_vhost *vhost;
+ struct vringh *vringh;
+ void __iomem *vq_addr;
+ unsigned int vq_size;
+ struct vring *vring;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ int ret;
+
+ vhost = to_epf_vhost(vdev);
+ vqueue = &vhost->vqueue[index];
+ reg = vhost->reg;
+ reg_queue = ®->vq[index];
+ epf = vhost->epf;
+ epc = epf->epc;
+ dev = &epf->dev;
+
+ vq = kzalloc(sizeof(*vq), GFP_KERNEL);
+ if (!vq)
+ return ERR_PTR(-ENOMEM);
+
+ vq->dev = vdev;
+ vq->callback = callback;
+ vq->num = num_bufs;
+ vq->index = index;
+ vq->notify = pci_epf_vhost_notify;
+ vq->type = VHOST_TYPE_MMIO;
+
+ vqueue->vq = vq;
+ vqueue->vhost = vhost;
+
+ vringh = &vq->vringh;
+ vring = &vringh->vring;
+ reg_queue->num_buffers = num_bufs;
+
+ vq_size = vring_size(num_bufs, VIRTIO_PCI_VRING_ALIGN);
+ vq_addr = pci_epc_mem_alloc_addr(epc, &vq_phys_addr, vq_size);
+ if (!vq_addr) {
+ dev_err(dev, "Failed to allocate virtqueue address\n");
+ ret = -ENOMEM;
+ goto err_mem_alloc_addr;
+ }
+
+ vring_init(vring, num_bufs, vq_addr, VIRTIO_PCI_VRING_ALIGN);
+ ret = vringh_init_mmio(vringh, 0, num_bufs, false, vring->desc,
+ vring->avail, vring->used);
+ if (ret) {
+ dev_err(dev, "Failed to init vringh\n");
+ goto err_init_mmio;
+ }
+
+ vqueue->phys_addr = vq_phys_addr;
+ vqueue->addr = vq_addr;
+ vqueue->size = vq_size;
+
+ INIT_DELAYED_WORK(&vqueue->cmd_handler, pci_epf_vhost_queue_cmd_handler);
+ queue_work(kpcivhost_workqueue, &vqueue->cmd_handler.work);
+
+ return vq;
+
+err_init_mmio:
+ pci_epc_mem_free_addr(epc, vq_phys_addr, vq_addr, vq_size);
+
+err_mem_alloc_addr:
+ kfree(vq);
+
+ return ERR_PTR(ret);
+}
+
+/* pci_epf_vhost_create_vqs - Create vhost virtqueues for vhost device
+ * @vdev: Vhost device that communicates with the remote virtio device
+ * @nvqs: Number of vhost virtqueues to be created
+ * @num_bufs: The number of buffers that should be supported by the vhost
+ * virtqueue (number of descriptors in the vhost virtqueue)
+ * @vqs: Pointers to all the created vhost virtqueues
+ * @callback: Callback function associated with the virtqueue
+ * @names: Names associated with each virtqueue
+ *
+ * Create vhost virtqueues for vhost device. This acts as a wrapper to
+ * pci_epf_vhost_create_vq() which creates individual vhost virtqueue.
+ */
+static int pci_epf_vhost_create_vqs(struct vhost_dev *vdev, unsigned int nvqs,
+ unsigned int num_bufs,
+ struct vhost_virtqueue *vqs[],
+ vhost_vq_callback_t *callbacks[],
+ const char * const names[])
+{
+ struct epf_vhost *vhost;
+ struct pci_epf *epf;
+ struct device *dev;
+ int ret, i;
+
+ vhost = to_epf_vhost(vdev);
+ epf = vhost->epf;
+ dev = &epf->dev;
+
+ for (i = 0; i < nvqs; i++) {
+ vqs[i] = pci_epf_vhost_create_vq(vdev, i, num_bufs,
+ callbacks[i]);
+ if (IS_ERR_OR_NULL(vqs[i])) {
+ ret = PTR_ERR(vqs[i]);
+ dev_err(dev, "Failed to create virtqueue\n");
+ goto err;
+ }
+ }
+
+ vdev->nvqs = nvqs;
+ vdev->vqs = vqs;
+
+ return 0;
+
+err:
+ pci_epf_vhost_del_vqs(vdev);
+ return ret;
+}
+
+/* pci_epf_vhost_set_features - vhost_config_ops to set vhost device features
+ * @vdev: Vhost device that communicates with the remote virtio device
+ * @features: Features supported by the vhost client driver
+ *
+ * vhost_config_ops invoked by the vhost client driver to set vhost device
+ * features.
+ */
+static int pci_epf_vhost_set_features(struct vhost_dev *vdev, u64 features)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+
+ vhost = to_epf_vhost(vdev);
+ reg = vhost->reg;
+
+ reg->host_features = features;
+
+ return 0;
+}
+
+/* ntb_vhost_set_status - vhost_config_ops to set vhost device status
+ * @vdev: Vhost device that communicates with the remote virtio device
+ * @status: Vhost device status configured by vhost client driver
+ *
+ * vhost_config_ops invoked by the vhost client driver to set vhost device
+ * status.
+ */
+static int pci_epf_vhost_set_status(struct vhost_dev *vdev, u8 status)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+
+ vhost = to_epf_vhost(vdev);
+ reg = vhost->reg;
+
+ reg->device_status = status;
+
+ return 0;
+}
+
+/* ntb_vhost_get_status - vhost_config_ops to get vhost device status
+ * @vdev: Vhost device that communicates with the remote virtio device
+ *
+ * vhost_config_ops invoked by the vhost client driver to get vhost device
+ * status set by the remote virtio driver.
+ */
+static u8 pci_epf_vhost_get_status(struct vhost_dev *vdev)
+{
+ struct epf_vhost_reg *reg;
+ struct epf_vhost *vhost;
+
+ vhost = to_epf_vhost(vdev);
+ reg = vhost->reg;
+
+ return reg->device_status;
+}
+
+static const struct vhost_config_ops pci_epf_vhost_ops = {
+ .create_vqs = pci_epf_vhost_create_vqs,
+ .del_vqs = pci_epf_vhost_del_vqs,
+ .write = pci_epf_vhost_write,
+ .read = pci_epf_vhost_read,
+ .set_features = pci_epf_vhost_set_features,
+ .set_status = pci_epf_vhost_set_status,
+ .get_status = pci_epf_vhost_get_status,
+};
+
+/* pci_epf_vhost_write_header - Write to PCIe standard configuration space
+ * header
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Invokes endpoint core framework's pci_epc_write_header() to write to the
+ * standard configuration space header.
+ */
+static int pci_epf_vhost_write_header(struct epf_vhost *vhost)
+{
+ struct pci_epf_header *header;
+ struct vhost_dev *vdev;
+ struct pci_epc *epc;
+ struct pci_epf *epf;
+ struct device *dev;
+ u8 func_no;
+ int ret;
+
+ vdev = &vhost->vdev;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+ func_no = epf->func_no;
+ header = epf->header;
+
+ ret = pci_epc_write_header(epc, func_no, header);
+ if (ret) {
+ dev_err(dev, "Configuration header write failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* pci_epf_vhost_release_dev - Callback function to free device
+ * @dev: Device in vhost_dev that has to be freed
+ *
+ * Callback function from device core invoked to free the device after
+ * all references have been removed. This frees the allocated memory for
+ * struct ntb_vhost.
+ */
+static void pci_epf_vhost_release_dev(struct device *dev)
+{
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+
+ vdev = to_vhost_dev(dev);
+ vhost = to_epf_vhost(vdev);
+
+ kfree(vhost);
+}
+
+/* pci_epf_vhost_register - Register a vhost device
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Invoked vhost_register_device() to register a vhost device after populating
+ * the deviceID and vendorID of the vhost device.
+ */
+static int pci_epf_vhost_register(struct epf_vhost *vhost)
+{
+ struct vhost_dev *vdev;
+ struct pci_epf *epf;
+ struct device *dev;
+ int ret;
+
+ vdev = &vhost->vdev;
+ epf = vhost->epf;
+ dev = &epf->dev;
+
+ vdev->dev.parent = dev;
+ vdev->dev.release = pci_epf_vhost_release_dev;
+ vdev->id.device = vhost->epf->header->subsys_id;
+ vdev->id.vendor = vhost->epf->header->subsys_vendor_id;
+ vdev->ops = &pci_epf_vhost_ops;
+
+ ret = vhost_register_device(vdev);
+ if (ret) {
+ dev_err(dev, "Failed to register vhost device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* pci_epf_vhost_configure_bar - Configure BAR of EPF device
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Allocate memory for the standard virtio configuration space and map it to
+ * the first free BAR.
+ */
+static int pci_epf_vhost_configure_bar(struct epf_vhost *vhost)
+{
+ size_t msix_table_size = 0, pba_size = 0, align, bar_size;
+ const struct pci_epc_features *epc_features;
+ struct pci_epf_bar *epf_bar;
+ struct vhost_dev *vdev;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ bool msix_capable;
+ u32 config_size;
+ int barno, ret;
+ void *base;
+ u64 size;
+
+ vdev = &vhost->vdev;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+
+ epc_features = vhost->epc_features;
+ barno = pci_epc_get_first_free_bar(epc_features);
+ if (barno < 0) {
+ dev_err(dev, "Failed to get free BAR\n");
+ return barno;
+ }
+
+ size = epc_features->bar_fixed_size[barno];
+ align = epc_features->align;
+ /* Check if epc_features is populated incorrectly */
+ if ((!IS_ALIGNED(size, align)))
+ return -EINVAL;
+
+ config_size = sizeof(struct epf_vhost_reg) + VHOST_DEVICE_CONFIG_SIZE;
+ config_size = ALIGN(config_size, 8);
+
+ msix_capable = epc_features->msix_capable;
+ if (msix_capable) {
+ msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
+ vhost->msix_table_offset = config_size;
+ vhost->msix_bar = barno;
+ /* Align to QWORD or 8 Bytes */
+ pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8);
+ }
+
+ bar_size = config_size + msix_table_size + pba_size;
+
+ if (!align)
+ bar_size = roundup_pow_of_two(bar_size);
+ else
+ bar_size = ALIGN(bar_size, align);
+
+ if (!size)
+ size = bar_size;
+ else if (size < bar_size)
+ return -EINVAL;
+
+ base = pci_epf_alloc_space(epf, size, barno, align,
+ PRIMARY_INTERFACE);
+ if (!base) {
+ dev_err(dev, "Failed to allocate configuration region\n");
+ return -ENOMEM;
+ }
+
+ epf_bar = &epf->bar[barno];
+ ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
+ if (ret) {
+ dev_err(dev, "Failed to set BAR: %d\n", barno);
+ goto err_set_bar;
+ }
+
+ vhost->reg = base;
+
+ return 0;
+
+err_set_bar:
+ pci_epf_free_space(epf, base, barno, PRIMARY_INTERFACE);
+
+ return ret;
+}
+
+/* pci_epf_vhost_configure_interrupts - Configure MSI/MSI-X capability of EPF
+ * device
+ * @vhost: EPF vhost containing the vhost device that communicates with the
+ * remote virtio device
+ *
+ * Configure MSI/MSI-X capability of EPF device. This will be used to interrupt
+ * the vhost virtqueue.
+ */
+static int pci_epf_vhost_configure_interrupts(struct epf_vhost *vhost)
+{
+ const struct pci_epc_features *epc_features;
+ struct pci_epf *epf;
+ struct pci_epc *epc;
+ struct device *dev;
+ int ret;
+
+ epc_features = vhost->epc_features;
+ epf = vhost->epf;
+ dev = &epf->dev;
+ epc = epf->epc;
+
+ if (epc_features->msi_capable) {
+ ret = pci_epc_set_msi(epc, epf->func_no,
+ EPF_VHOST_MAX_INTERRUPTS);
+ if (ret) {
+ dev_err(dev, "MSI configuration failed\n");
+ return ret;
+ }
+ }
+
+ if (epc_features->msix_capable) {
+ ret = pci_epc_set_msix(epc, epf->func_no,
+ EPF_VHOST_MAX_INTERRUPTS,
+ vhost->msix_bar,
+ vhost->msix_table_offset);
+ if (ret) {
+ dev_err(dev, "MSI-X configuration failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* pci_epf_vhost_cfs_link - Link vhost client driver with EPF vhost to get
+ * the deviceID and driverID to be writtent to the PCIe config space
+ * @epf_vhost_item: Config item representing the EPF vhost created by this
+ * driver
+ * @epf: Endpoint function device that is bound to the endpoint controller
+ *
+ * This is invoked when the user creates a softlink between the vhost client
+ * to the EPF vhost. This gets the deviceID and vendorID data from the vhost
+ * client and copies it to the subys_id and subsys_vendor_id of the EPF
+ * header. This will be used by the remote virtio to bind a virtio client
+ * driver.
+ */
+static int pci_epf_vhost_cfs_link(struct config_item *epf_vhost_item,
+ struct config_item *driver_item)
+{
+ struct vhost_driver_item *vdriver_item;
+ struct epf_vhost *vhost;
+
+ vdriver_item = to_vhost_driver_item(driver_item);
+ vhost = to_epf_vhost_from_ci(epf_vhost_item);
+
+ vhost->epf->header->subsys_id = vdriver_item->device;
+ vhost->epf->header->subsys_vendor_id = vdriver_item->vendor;
+
+ return 0;
+}
+
+static struct configfs_item_operations pci_epf_vhost_cfs_ops = {
+ .allow_link = pci_epf_vhost_cfs_link,
+};
+
+static const struct config_item_type pci_epf_vhost_cfs_type = {
+ .ct_item_ops = &pci_epf_vhost_cfs_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* pci_epf_vhost_cfs_work - Delayed work function to create configfs directory
+ * to perform EPF vhost specific initializations
+ * @work: The work_struct holding the pci_epf_vhost_cfs_work() function that
+ * is scheduled
+ *
+ * This is a delayed work function to create configfs directory to perform EPF
+ * vhost specific initializations. This configfs directory will be a
+ * sub-directory to the directory created by the user to create pci_epf device.
+ */
+static void pci_epf_vhost_cfs_work(struct work_struct *work)
+{
+ struct epf_vhost *vhost = container_of(work, struct epf_vhost,
+ cfs_work.work);
+ struct pci_epf *epf = vhost->epf;
+ struct device *dev = &epf->dev;
+ struct config_group *group;
+ struct vhost_dev *vdev;
+ int ret;
+
+ if (!epf->group) {
+ queue_delayed_work(kpcivhost_workqueue, &vhost->cfs_work,
+ msecs_to_jiffies(50));
+ return;
+ }
+
+ vdev = &vhost->vdev;
+ group = &vhost->group;
+ config_group_init_type_name(group, dev_name(dev), &pci_epf_vhost_cfs_type);
+ ret = configfs_register_group(epf->group, group);
+ if (ret) {
+ dev_err(dev, "Failed to register configfs group %s\n", dev_name(dev));
+ return;
+ }
+}
+
+/* pci_epf_vhost_probe - Initialize struct epf_vhost when a new EPF device is
+ * created
+ * @epf: Endpoint function device that is bound to this driver
+ *
+ * Probe function to initialize struct epf_vhost when a new EPF device is
+ * created.
+ */
+static int pci_epf_vhost_probe(struct pci_epf *epf)
+{
+ struct epf_vhost *vhost;
+
+ vhost = kzalloc(sizeof(*vhost), GFP_KERNEL);
+ if (!vhost)
+ return -ENOMEM;
+
+ epf->header = &epf_vhost_header;
+ vhost->epf = epf;
+
+ epf_set_drvdata(epf, vhost);
+ INIT_DELAYED_WORK(&vhost->cmd_handler, pci_epf_vhost_cmd_handler);
+ INIT_DELAYED_WORK(&vhost->cfs_work, pci_epf_vhost_cfs_work);
+ queue_delayed_work(kpcivhost_workqueue, &vhost->cfs_work,
+ msecs_to_jiffies(50));
+
+ return 0;
+}
+
+/* pci_epf_vhost_remove - Free the initializations performed by
+ * pci_epf_vhost_probe()
+ * @epf: Endpoint function device that is bound to this driver
+ *
+ * Free the initializations performed by pci_epf_vhost_probe().
+ */
+static int pci_epf_vhost_remove(struct pci_epf *epf)
+{
+ struct epf_vhost *vhost;
+
+ vhost = epf_get_drvdata(epf);
+ cancel_delayed_work_sync(&vhost->cfs_work);
+
+ return 0;
+}
+
+/* pci_epf_vhost_bind - Bind callback to initialize the PCIe EP controller
+ * @epf: Endpoint function device that is bound to the endpoint controller
+ *
+ * pci_epf_vhost_bind() is invoked when an endpoint controller is bound to
+ * endpoint function. This function initializes the endpoint controller
+ * with vhost endpoint function specific data.
+ */
+static int pci_epf_vhost_bind(struct pci_epf *epf)
+{
+ const struct pci_epc_features *epc_features;
+ struct epf_vhost *vhost;
+ struct pci_epc *epc;
+ struct device *dev;
+ int ret;
+
+ vhost = epf_get_drvdata(epf);
+ dev = &epf->dev;
+ epc = epf->epc;
+
+ epc_features = pci_epc_get_features(epc, epf->func_no);
+ if (!epc_features) {
+ dev_err(dev, "Fail to get EPC features\n");
+ return -EINVAL;
+ }
+ vhost->epc_features = epc_features;
+
+ ret = pci_epf_vhost_write_header(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to bind VHOST config header\n");
+ return ret;
+ }
+
+ ret = pci_epf_vhost_configure_bar(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to configure BAR\n");
+ return ret;
+ }
+
+ ret = pci_epf_vhost_configure_interrupts(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to configure BAR\n");
+ return ret;
+ }
+
+ ret = pci_epf_vhost_register(vhost);
+ if (ret) {
+ dev_err(dev, "Failed to bind VHOST config header\n");
+ return ret;
+ }
+
+ queue_work(kpcivhost_workqueue, &vhost->cmd_handler.work);
+
+ return ret;
+}
+
+/* pci_epf_vhost_unbind - Inbind callback to cleanup the PCIe EP controller
+ * @epf: Endpoint function device that is bound to the endpoint controller
+ *
+ * pci_epf_vhost_unbind() is invoked when the binding between endpoint
+ * controller is removed from endpoint function. This will unregister vhost
+ * device and cancel pending cmd_handler work.
+ */
+static void pci_epf_vhost_unbind(struct pci_epf *epf)
+{
+ struct epf_vhost *vhost;
+ struct vhost_dev *vdev;
+
+ vhost = epf_get_drvdata(epf);
+ vdev = &vhost->vdev;
+
+ cancel_delayed_work_sync(&vhost->cmd_handler);
+ if (device_is_registered(&vdev->dev))
+ vhost_unregister_device(vdev);
+}
+
+static struct pci_epf_ops epf_ops = {
+ .bind = pci_epf_vhost_bind,
+ .unbind = pci_epf_vhost_unbind,
+};
+
+static const struct pci_epf_device_id pci_epf_vhost_ids[] = {
+ {
+ .name = "pci-epf-vhost",
+ },
+ { },
+};
+
+static struct pci_epf_driver epf_vhost_driver = {
+ .driver.name = "pci_epf_vhost",
+ .probe = pci_epf_vhost_probe,
+ .remove = pci_epf_vhost_remove,
+ .id_table = pci_epf_vhost_ids,
+ .ops = &epf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int __init pci_epf_vhost_init(void)
+{
+ int ret;
+
+ kpcivhost_workqueue = alloc_workqueue("kpcivhost", WQ_MEM_RECLAIM |
+ WQ_HIGHPRI, 0);
+ ret = pci_epf_register_driver(&epf_vhost_driver);
+ if (ret) {
+ pr_err("Failed to register pci epf vhost driver --> %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+module_init(pci_epf_vhost_init);
+
+static void __exit pci_epf_vhost_exit(void)
+{
+ pci_epf_unregister_driver(&epf_vhost_driver);
+}
+module_exit(pci_epf_vhost_exit);
+
+MODULE_DESCRIPTION("PCI EPF VHOST DRIVER");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/vhost/vhost_cfs.c b/drivers/vhost/vhost_cfs.c
index ae46e71968f1..ab0393289200 100644
--- a/drivers/vhost/vhost_cfs.c
+++ b/drivers/vhost/vhost_cfs.c
@@ -18,12 +18,6 @@ static struct config_group *vhost_driver_group;
/* VHOST device like PCIe EP, NTB etc., */
static struct config_group *vhost_device_group;
-struct vhost_driver_item {
- struct config_group group;
- u32 vendor;
- u32 device;
-};
-
struct vhost_driver_group {
struct config_group group;
};
@@ -33,13 +27,6 @@ struct vhost_device_item {
struct vhost_dev *vdev;
};
-static inline
-struct vhost_driver_item *to_vhost_driver_item(struct config_item *item)
-{
- return container_of(to_config_group(item), struct vhost_driver_item,
- group);
-}
-
static inline
struct vhost_device_item *to_vhost_device_item(struct config_item *item)
{
diff --git a/include/linux/vhost.h b/include/linux/vhost.h
index be9341ffd266..640650311310 100644
--- a/include/linux/vhost.h
+++ b/include/linux/vhost.h
@@ -74,6 +74,7 @@ struct vhost_virtqueue {
struct vhost_dev *dev;
enum vhost_type type;
struct vringh vringh;
+ int index;
void (*callback)(struct vhost_virtqueue *vq);
void (*notify)(struct vhost_virtqueue *vq);
@@ -148,6 +149,12 @@ struct vhost_msg_node {
struct list_head node;
};
+struct vhost_driver_item {
+ struct config_group group;
+ u32 vendor;
+ u32 device;
+};
+
enum vhost_notify_event {
NOTIFY_SET_STATUS,
NOTIFY_FINALIZE_FEATURES,
@@ -230,6 +237,13 @@ static inline void *vhost_get_drvdata(struct vhost_dev *vdev)
return dev_get_drvdata(&vdev->dev);
}
+static inline
+struct vhost_driver_item *to_vhost_driver_item(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct vhost_driver_item,
+ group);
+}
+
int vhost_register_driver(struct vhost_driver *driver);
void vhost_unregister_driver(struct vhost_driver *driver);
int vhost_register_device(struct vhost_dev *vdev);
--
2.17.1
next prev parent reply other threads:[~2020-07-02 8:24 UTC|newest]
Thread overview: 96+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-07-02 8:21 [RFC PATCH 00/22] Enhance VHOST to enable SoC-to-SoC communication Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 01/22] vhost: Make _feature_ bits a property of vhost device Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 02/22] vhost: Introduce standard Linux driver model in VHOST Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 03/22] vhost: Add ops for the VHOST driver to configure VHOST device Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 04/22] vringh: Add helpers to access vring in MMIO Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 05/22] vhost: Add MMIO helpers for operations on vhost virtqueue Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 06/22] vhost: Introduce configfs entry for configuring VHOST Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 07/22] virtio_pci: Use request_threaded_irq() instead of request_irq() Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 08/22] rpmsg: virtio_rpmsg_bus: Disable receive virtqueue callback when reading messages Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 09/22] rpmsg: Introduce configfs entry for configuring rpmsg Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 10/22] rpmsg: virtio_rpmsg_bus: Add Address Service Notification support Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 11/22] rpmsg: virtio_rpmsg_bus: Move generic rpmsg structure to rpmsg_internal.h Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 12/22] virtio: Add ops to allocate and free buffer Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 13/22] rpmsg: virtio_rpmsg_bus: Use virtio_alloc_buffer() and virtio_free_buffer() Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 14/22] rpmsg: Add VHOST based remote processor messaging bus Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 15/22] samples/rpmsg: Setup delayed work to send message Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 16/22] samples/rpmsg: Wait for address to be bound to rpdev for sending message Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 17/22] rpmsg.txt: Add Documentation to configure rpmsg using configfs Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 18/22] virtio_pci: Add VIRTIO driver for VHOST on Configurable PCIe Endpoint device Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I [this message]
2020-07-02 8:21 ` [RFC PATCH 19/22] PCI: endpoint: Add EP function driver to provide VHOST interface Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 20/22] NTB: Add a new NTB client driver to implement VIRTIO functionality Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 21/22] NTB: Add a new NTB client driver to implement VHOST functionality Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 8:21 ` [RFC PATCH 22/22] NTB: Describe ntb_virtio and ntb_vhost client in the documentation Kishon Vijay Abraham I
2020-07-02 8:21 ` Kishon Vijay Abraham I
2020-07-02 9:51 ` [RFC PATCH 00/22] Enhance VHOST to enable SoC-to-SoC communication Michael S. Tsirkin
2020-07-02 10:10 ` Jason Wang
2020-07-02 13:35 ` Kishon Vijay Abraham I
2020-07-02 13:35 ` Kishon Vijay Abraham I
2020-07-03 7:16 ` Jason Wang
2020-07-03 7:16 ` Jason Wang
2020-07-06 9:32 ` Kishon Vijay Abraham I
2020-07-06 9:32 ` Kishon Vijay Abraham I
2020-07-07 9:47 ` Jason Wang
2020-07-07 9:47 ` Jason Wang
2020-07-07 14:45 ` Kishon Vijay Abraham I
2020-07-07 14:45 ` Kishon Vijay Abraham I
2020-07-08 11:22 ` Jason Wang
2020-07-08 13:13 ` Kishon Vijay Abraham I
2020-07-08 13:13 ` Kishon Vijay Abraham I
2020-07-09 6:26 ` Jason Wang
2020-08-28 10:34 ` Cornelia Huck
2020-08-28 10:34 ` Cornelia Huck
2020-09-01 5:24 ` Kishon Vijay Abraham I
2020-09-01 5:24 ` Kishon Vijay Abraham I
2020-09-01 8:50 ` Jason Wang
2020-09-01 8:50 ` Jason Wang
2020-09-08 16:37 ` Cornelia Huck
2020-09-08 16:37 ` Cornelia Huck
2020-09-09 8:41 ` Jason Wang
2020-09-09 8:41 ` Jason Wang
2020-09-14 7:23 ` Kishon Vijay Abraham I
2020-09-14 7:23 ` Kishon Vijay Abraham I
2020-09-15 8:18 ` Jason Wang
2020-09-15 8:18 ` Jason Wang
2020-09-15 15:47 ` Kishon Vijay Abraham I
2020-09-15 15:47 ` Kishon Vijay Abraham I
2020-09-16 3:10 ` Jason Wang
2020-09-16 3:10 ` Jason Wang
2020-09-16 11:47 ` Kishon Vijay Abraham I
2020-09-16 11:47 ` Kishon Vijay Abraham I
2020-09-16 11:47 ` Kishon Vijay Abraham I
2020-09-16 11:47 ` Kishon Vijay Abraham I
2020-09-18 4:04 ` Jason Wang
2020-09-18 4:04 ` Jason Wang
2020-07-15 17:15 ` Mathieu Poirier
2020-09-01 4:40 ` Kishon Vijay Abraham I
2020-09-01 4:40 ` Kishon Vijay Abraham I
2020-07-02 10:25 ` Kishon Vijay Abraham I
2020-07-02 10:25 ` Kishon Vijay Abraham I
2020-07-02 17:31 ` Mathieu Poirier
2020-07-02 17:31 ` Mathieu Poirier
2020-07-03 6:17 ` Kishon Vijay Abraham I
2020-07-03 6:17 ` Kishon Vijay Abraham I
2020-07-03 6:17 ` Kishon Vijay Abraham I
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200702082143.25259-20-kishon@ti.com \
--to=kishon@ti.com \
--cc=allenbh@gmail.com \
--cc=bhelgaas@google.com \
--cc=bjorn.andersson@linaro.org \
--cc=dave.jiang@intel.com \
--cc=jasowang@redhat.com \
--cc=jdmason@kudzu.us \
--cc=kvm@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-ntb@googlegroups.com \
--cc=linux-pci@vger.kernel.org \
--cc=linux-remoteproc@vger.kernel.org \
--cc=lorenzo.pieralisi@arm.com \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=ohad@wizery.com \
--cc=pbonzini@redhat.com \
--cc=sgarzare@redhat.com \
--cc=stefanha@redhat.com \
--cc=virtualization@lists.linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.