lkml.org 
[lkml]   [2020]   [Jul]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 18/22] virtio_pci: Add VIRTIO driver for VHOST on Configurable PCIe Endpoint device
    Date
    Add VIRTIO driver to support Linux VHOST on Configurable PCIe Endpoint
    device in the backend.

    Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
    ---
    drivers/virtio/Kconfig | 9 +
    drivers/virtio/Makefile | 1 +
    drivers/virtio/virtio_pci_epf.c | 670 ++++++++++++++++++++++++++++++++
    include/linux/virtio.h | 3 +
    4 files changed, 683 insertions(+)
    create mode 100644 drivers/virtio/virtio_pci_epf.c

    diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
    index 69a32dfc318a..4b251f8482ae 100644
    --- a/drivers/virtio/Kconfig
    +++ b/drivers/virtio/Kconfig
    @@ -43,6 +43,15 @@ config VIRTIO_PCI_LEGACY

    If unsure, say Y.

    +config VIRTIO_PCI_EPF
    + bool "Support for virtio device on configurable PCIe Endpoint"
    + depends on VIRTIO_PCI
    + help
    + Select this configuration option to enable the VIRTIO driver
    + for configurable PCIe Endpoint running Linux in backend.
    +
    + If in doubt, say "N" to disable VIRTIO PCI EPF driver.
    +
    config VIRTIO_VDPA
    tristate "vDPA driver for virtio devices"
    depends on VDPA
    diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
    index 29a1386ecc03..08a158365d5e 100644
    --- a/drivers/virtio/Makefile
    +++ b/drivers/virtio/Makefile
    @@ -4,6 +4,7 @@ obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
    obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
    virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
    virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
    +virtio_pci-$(CONFIG_VIRTIO_PCI_EPF) += virtio_pci_epf.o
    obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
    obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
    obj-$(CONFIG_VIRTIO_VDPA) += virtio_vdpa.o
    diff --git a/drivers/virtio/virtio_pci_epf.c b/drivers/virtio/virtio_pci_epf.c
    new file mode 100644
    index 000000000000..20e53869f179
    --- /dev/null
    +++ b/drivers/virtio/virtio_pci_epf.c
    @@ -0,0 +1,670 @@
    +// SPDX-License-Identifier: GPL-2.0
    +/**
    + * Virtio PCI driver - Configurable PCIe Endpoint device Support
    + *
    + * The Configurable PCIe Endpoint device is present on an another system running
    + * Linux and configured using drivers/pci/endpoint/functions/pci-epf-vhost.c
    + *
    + * Copyright (C) 2020 Texas Instruments
    + * Author: Kishon Vijay Abraham I <kishon@ti.com>
    + */
    +
    +#include <linux/delay.h>
    +#include "virtio_pci_common.h"
    +
    +#define DRV_MODULE_NAME "virtio-pci-epf"
    +
    +#define HOST_FEATURES_LOWER 0x00
    +#define HOST_FEATURES_UPPER 0x04
    +#define GUEST_FEATURES_LOWER 0x08
    +#define GUEST_FEATURES_UPPER 0x0C
    +#define MSIX_CONFIG 0x10
    +#define NUM_QUEUES 0x12
    +#define DEVICE_STATUS 0x14
    +#define CONFIG_GENERATION 0x15
    +#define ISR 0x16
    +#define HOST_CMD 0x1A
    +enum host_cmd {
    + HOST_CMD_NONE,
    + HOST_CMD_SET_STATUS,
    + HOST_CMD_FINALIZE_FEATURES,
    + HOST_CMD_RESET,
    +};
    +
    +#define HOST_CMD_STATUS 0x1B
    +enum host_cmd_status {
    + HOST_CMD_STATUS_NONE,
    + HOST_CMD_STATUS_OKAY,
    + HOST_CMD_STATUS_ERROR,
    +};
    +
    +#define QUEUE_BASE 0x1C
    +#define CMD 0x00
    +enum queue_cmd {
    + QUEUE_CMD_NONE,
    + QUEUE_CMD_ACTIVATE,
    + QUEUE_CMD_DEACTIVATE,
    + QUEUE_CMD_NOTIFY,
    +};
    +
    +#define CMD_STATUS 0x01
    +enum queue_cmd_status {
    + QUEUE_CMD_STATUS_NONE,
    + QUEUE_CMD_STATUS_OKAY,
    + QUEUE_CMD_STATUS_ERROR,
    +};
    +
    +#define STATUS 0x2
    +#define STATUS_ACTIVE BIT(0)
    +
    +#define NUM_BUFFERS 0x04
    +#define MSIX_VECTOR 0x06
    +#define ADDR_LOWER 0x08
    +#define ADDR_UPPER 0x0C
    +#define QUEUE_CMD(n) (QUEUE_BASE + CMD + (n) * 16)
    +#define QUEUE_CMD_STATUS(n) (QUEUE_BASE + CMD_STATUS + (n) * 16)
    +#define QUEUE_STATUS(n) (QUEUE_BASE + STATUS + (n) * 16)
    +#define QUEUE_NUM_BUFFERS(n) (QUEUE_BASE + NUM_BUFFERS + (n) * 16)
    +#define QUEUE_MSIX_VECTOR(n) (QUEUE_BASE + MSIX_VECTOR + (n) * 16)
    +#define QUEUE_ADDR_LOWER(n) (QUEUE_BASE + ADDR_LOWER + (n) * 16)
    +#define QUEUE_ADDR_UPPER(n) (QUEUE_BASE + ADDR_UPPER + (n) * 16)
    +
    +#define DEVICE_CFG_SPACE 0x110
    +
    +#define COMMAND_TIMEOUT 1000 /* 1 Sec */
    +
    +struct virtio_pci_epf {
    + /* mutex to protect sending commands to EPF vhost */
    + struct mutex lock;
    + struct virtio_pci_device vp_dev;
    +};
    +
    +#define to_virtio_pci_epf(dev) container_of((dev), struct virtio_pci_epf, vp_dev)
    +
    +/* virtio_pci_epf_send_command - Send commands to the remote EPF device running
    + * vhost driver
    + * @vp_dev: Virtio PCIe device that communicates with the endpoint device
    + * @command: The command that has to be sent to the remote endpoint device
    + *
    + * Helper function to send commands to the remote endpoint function device
    + * running vhost driver.
    + */
    +static int virtio_pci_epf_send_command(struct virtio_pci_device *vp_dev,
    + u32 command)
    +{
    + struct virtio_pci_epf *pci_epf;
    + void __iomem *ioaddr;
    + ktime_t timeout;
    + bool timedout;
    + int ret = 0;
    + u8 status;
    +
    + pci_epf = to_virtio_pci_epf(vp_dev);
    + ioaddr = vp_dev->ioaddr;
    +
    + mutex_lock(&pci_epf->lock);
    + writeb(command, ioaddr + HOST_CMD);
    + timeout = ktime_add_ms(ktime_get(), COMMAND_TIMEOUT);
    + while (1) {
    + timedout = ktime_after(ktime_get(), timeout);
    + status = readb(ioaddr + HOST_CMD_STATUS);
    +
    + if (status == HOST_CMD_STATUS_ERROR) {
    + ret = -EINVAL;
    + break;
    + }
    +
    + if (status == HOST_CMD_STATUS_OKAY)
    + break;
    +
    + if (WARN_ON(timedout)) {
    + ret = -ETIMEDOUT;
    + break;
    + }
    +
    + usleep_range(5, 10);
    + }
    +
    + writeb(HOST_CMD_STATUS_NONE, ioaddr + HOST_CMD_STATUS);
    + mutex_unlock(&pci_epf->lock);
    +
    + return ret;
    +}
    +
    +/* virtio_pci_epf_send_queue_command - Send commands to the remote EPF device
    + * for configuring virtqueue
    + * @vp_dev: Virtio PCIe device that communicates with the endpoint device
    + * @vq: The virtqueue that has to be configured on the remote endpoint device
    + * @command: The command that has to be sent to the remote endpoint device
    + *
    + * Helper function to send commands to the remote endpoint function device for
    + * configuring the virtqueue.
    + */
    +static int virtio_pci_epf_send_queue_command(struct virtio_pci_device *vp_dev,
    + struct virtqueue *vq, u8 command)
    +{
    + void __iomem *ioaddr;
    + ktime_t timeout;
    + bool timedout;
    + int ret = 0;
    + u8 status;
    +
    + ioaddr = vp_dev->ioaddr;
    +
    + mutex_lock(&vq->lock);
    + writeb(command, ioaddr + QUEUE_CMD(vq->index));
    + timeout = ktime_add_ms(ktime_get(), COMMAND_TIMEOUT);
    + while (1) {
    + timedout = ktime_after(ktime_get(), timeout);
    + status = readb(ioaddr + QUEUE_CMD_STATUS(vq->index));
    +
    + if (status == QUEUE_CMD_STATUS_ERROR) {
    + ret = -EINVAL;
    + break;
    + }
    +
    + if (status == QUEUE_CMD_STATUS_OKAY)
    + break;
    +
    + if (WARN_ON(timedout)) {
    + ret = -ETIMEDOUT;
    + break;
    + }
    +
    + usleep_range(5, 10);
    + }
    +
    + writeb(QUEUE_CMD_STATUS_NONE, ioaddr + QUEUE_CMD_STATUS(vq->index));
    + mutex_unlock(&vq->lock);
    +
    + return ret;
    +}
    +
    +/* virtio_pci_epf_get_features - virtio_config_ops to get EPF vhost device
    + * features
    + * @vdev: Virtio device that communicates with the remote EPF vhost device
    + *
    + * virtio_config_ops to get EPF vhost device features. The device features
    + * are accessed using BAR mapped registers.
    + */
    +static u64 virtio_pci_epf_get_features(struct virtio_device *vdev)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    + u64 features;
    +
    + vp_dev = to_vp_device(vdev);
    + ioaddr = vp_dev->ioaddr;
    +
    + features = readl(ioaddr + HOST_FEATURES_UPPER);
    + features <<= 32;
    + features |= readl(ioaddr + HOST_FEATURES_LOWER);
    +
    + return features;
    +}
    +
    +/* virtio_pci_epf_finalize_features - virtio_config_ops to finalize features
    + * with remote EPF vhost device
    + * @vdev: Virtio device that communicates with the remote vhost device
    + *
    + * Indicate the negotiated features to the remote EPF vhost device by sending
    + * HOST_CMD_FINALIZE_FEATURES command.
    + */
    +static int virtio_pci_epf_finalize_features(struct virtio_device *vdev)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    + struct device *dev;
    + int ret;
    +
    + vp_dev = to_vp_device(vdev);
    + dev = &vp_dev->pci_dev->dev;
    + ioaddr = vp_dev->ioaddr;
    +
    + /* Give virtio_ring a chance to accept features. */
    + vring_transport_features(vdev);
    +
    + writel(lower_32_bits(vdev->features), ioaddr + GUEST_FEATURES_LOWER);
    + writel(upper_32_bits(vdev->features), ioaddr + GUEST_FEATURES_UPPER);
    +
    + ret = virtio_pci_epf_send_command(vp_dev, HOST_CMD_FINALIZE_FEATURES);
    + if (ret) {
    + dev_err(dev, "Failed to set configuration event vector\n");
    + return VIRTIO_MSI_NO_VECTOR;
    + }
    +
    + return 0;
    +}
    +
    +/* virtio_pci_epf_get - Copy the device configuration space data from
    + * EPF device to buffer provided by virtio driver
    + * @vdev: Virtio device that communicates with the remote vhost device
    + * @offset: Offset in the device configuration space
    + * @buf: Buffer address from virtio driver where configuration space
    + * data has to be copied
    + * @len: Length of the data from device configuration space to be copied
    + *
    + * Copy the device configuration space data to buffer provided by virtio
    + * driver.
    + */
    +static void virtio_pci_epf_get(struct virtio_device *vdev, unsigned int offset,
    + void *buf, unsigned int len)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    +
    + vp_dev = to_vp_device(vdev);
    + ioaddr = vp_dev->ioaddr;
    +
    + memcpy_fromio(buf, ioaddr + DEVICE_CFG_SPACE + offset, len);
    +}
    +
    +/* virtio_pci_epf_set - Copy the device configuration space data from buffer
    + * provided by virtio driver to EPF device
    + * @vdev: Virtio device that communicates with the remote vhost device
    + * @offset: Offset in the device configuration space
    + * @buf: Buffer address provided by virtio driver which has the configuration
    + * space data to be copied
    + * @len: Length of the data from device configuration space to be copied
    + *
    + * Copy the device configuration space data from buffer provided by virtio
    + * driver to the EPF device.
    + */
    +static void virtio_pci_epf_set(struct virtio_device *vdev, unsigned int offset,
    + const void *buf, unsigned int len)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    +
    + vp_dev = to_vp_device(vdev);
    + ioaddr = vp_dev->ioaddr;
    +
    + memcpy_toio(ioaddr + DEVICE_CFG_SPACE + offset, buf, len);
    +}
    +
    +/* virtio_pci_epf_get_status - EPF virtio_config_ops to get device status
    + * @vdev: Virtio device that communicates with the remote vhost device
    + *
    + * EPF virtio_config_ops to get device status. The remote EPF vhost device
    + * populates the vhost device status in BAR mapped region.
    + */
    +static u8 virtio_pci_epf_get_status(struct virtio_device *vdev)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    +
    + vp_dev = to_vp_device(vdev);
    + ioaddr = vp_dev->ioaddr;
    +
    + return readb(ioaddr + DEVICE_STATUS);
    +}
    +
    +/* virtio_pci_epf_set_status - EPF virtio_config_ops to set device status
    + * @vdev: Virtio device that communicates with the remote vhost device
    + *
    + * EPF virtio_config_ops to set device status. This function updates the
    + * status in scratchpad register and sends a notification to the vhost
    + * device using HOST_CMD_SET_STATUS command.
    + */
    +static void virtio_pci_epf_set_status(struct virtio_device *vdev, u8 status)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    + struct device *dev;
    + int ret;
    +
    + vp_dev = to_vp_device(vdev);
    + dev = &vp_dev->pci_dev->dev;
    + ioaddr = vp_dev->ioaddr;
    +
    + /* We should never be setting status to 0. */
    + if (WARN_ON(!status))
    + return;
    +
    + writeb(status, ioaddr + DEVICE_STATUS);
    +
    + ret = virtio_pci_epf_send_command(vp_dev, HOST_CMD_SET_STATUS);
    + if (ret)
    + dev_err(dev, "Failed to set device status\n");
    +}
    +
    +/* virtio_pci_epf_reset - EPF virtio_config_ops to reset the device
    + * @vdev: Virtio device that communicates with the remote vhost device
    + *
    + * EPF virtio_config_ops to reset the device. This sends HOST_CMD_RESET
    + * command to reset the device.
    + */
    +static void virtio_pci_epf_reset(struct virtio_device *vdev)
    +{
    + struct virtio_pci_device *vp_dev;
    + void __iomem *ioaddr;
    + struct device *dev;
    + int ret;
    +
    + vp_dev = to_vp_device(vdev);
    + dev = &vp_dev->pci_dev->dev;
    + ioaddr = vp_dev->ioaddr;
    +
    + ret = virtio_pci_epf_send_command(vp_dev, HOST_CMD_RESET);
    + if (ret)
    + dev_err(dev, "Failed to reset device\n");
    +}
    +
    +/* virtio_pci_epf_config_vector - virtio_pci_device ops to set config vector
    + * @vp_dev: Virtio PCI device managed by virtio_pci_common.c
    + *
    + * virtio_pci_device ops to set config vector. This writes the config MSI-X
    + * vector to the BAR mapped region.
    + */
    +static u16 virtio_pci_epf_config_vector(struct virtio_pci_device *vp_dev,
    + u16 vector)
    +{
    + void __iomem *ioaddr = vp_dev->ioaddr;
    +
    + writew(vector, ioaddr + MSIX_CONFIG);
    +
    + return readw(ioaddr + MSIX_CONFIG);
    +}
    +
    +/* virtio_pci_epf_notify - Send notification to the remote vhost virtqueue
    + * @vq: The local virtio virtqueue corresponding to the remote vhost virtqueue
    + * where the notification has to be sent
    + *
    + * Send notification to the remote vhost virtqueue by using QUEUE_CMD_NOTIFY
    + * command.
    + */
    +static bool virtio_pci_epf_notify(struct virtqueue *vq)
    +{
    + struct virtio_pci_device *vp_dev;
    + struct device *dev;
    + int ret;
    +
    + vp_dev = vq->priv;
    + dev = &vp_dev->pci_dev->dev;
    +
    + ret = virtio_pci_epf_send_queue_command(vp_dev, vq, QUEUE_CMD_NOTIFY);
    + if (ret) {
    + dev_err(dev, "Notifying virtqueue: %d Failed\n", vq->index);
    + return false;
    + }
    +
    + return true;
    +}
    +
    +/* virtio_pci_epf_setup_vq - Configure virtqueue
    + * @vp_dev: Virtio PCI device managed by virtio_pci_common.c
    + * @info: Wrapper to virtqueue to maintain list of all queues
    + * @callback: Callback function that has to be associated with virtqueue
    + * @vq: The local virtio virtqueue corresponding to the remote vhost virtqueue
    + * where the notification has to be sent
    + *
    + * Configure virtqueue with the number of buffers provided by EPF vhost device
    + * and associate a callback function for each of these virtqueues.
    + */
    +static struct virtqueue *
    +virtio_pci_epf_setup_vq(struct virtio_pci_device *vp_dev,
    + struct virtio_pci_vq_info *info, unsigned int index,
    + void (*callback)(struct virtqueue *vq),
    + const char *name, bool ctx, u16 msix_vec)
    +{
    + u16 queue_num_buffers;
    + void __iomem *ioaddr;
    + struct virtqueue *vq;
    + struct device *dev;
    + dma_addr_t vq_addr;
    + u16 status;
    + int err;
    +
    + dev = &vp_dev->pci_dev->dev;
    + ioaddr = vp_dev->ioaddr;
    +
    + status = readw(ioaddr + QUEUE_STATUS(index));
    + if (status & STATUS_ACTIVE) {
    + dev_err(dev, "Virtqueue %d is already active\n", index);
    + return ERR_PTR(-ENOENT);
    + }
    +
    + queue_num_buffers = readw(ioaddr + QUEUE_NUM_BUFFERS(index));
    + if (!queue_num_buffers) {
    + dev_err(dev, "Virtqueue %d is not available\n", index);
    + return ERR_PTR(-ENOENT);
    + }
    +
    + info->msix_vector = msix_vec;
    +
    + vq = vring_create_virtqueue(index, queue_num_buffers,
    + VIRTIO_PCI_VRING_ALIGN, &vp_dev->vdev, true,
    + false, ctx, virtio_pci_epf_notify, callback,
    + name);
    + if (!vq) {
    + dev_err(dev, "Failed to create Virtqueue %d\n", index);
    + return ERR_PTR(-ENOMEM);
    + }
    + mutex_init(&vq->lock);
    +
    + vq_addr = virtqueue_get_desc_addr(vq);
    + writel(lower_32_bits(vq_addr), ioaddr + QUEUE_ADDR_LOWER(index));
    + writel(upper_32_bits(vq_addr), ioaddr + QUEUE_ADDR_UPPER(index));
    +
    + vq->priv = vp_dev;
    + writew(QUEUE_CMD_ACTIVATE, ioaddr + QUEUE_CMD(index));
    +
    + err = virtio_pci_epf_send_queue_command(vp_dev, vq, QUEUE_CMD_ACTIVATE);
    + if (err) {
    + dev_err(dev, "Failed to activate Virtqueue %d\n", index);
    + goto out_del_vq;
    + }
    +
    + if (msix_vec != VIRTIO_MSI_NO_VECTOR)
    + writew(msix_vec, ioaddr + QUEUE_MSIX_VECTOR(index));
    +
    + return vq;
    +
    +out_del_vq:
    + vring_del_virtqueue(vq);
    +
    + return ERR_PTR(err);
    +}
    +
    +/* virtio_pci_epf_del_vq - Free memory allocated for virtio virtqueues
    + * @info: Wrapper to virtqueue to maintain list of all queues
    + *
    + * Free memory allocated for a virtqueue represented by @info
    + */
    +static void virtio_pci_epf_del_vq(struct virtio_pci_vq_info *info)
    +{
    + struct virtio_pci_device *vp_dev;
    + struct virtqueue *vq;
    + void __iomem *ioaddr;
    + unsigned int index;
    +
    + vq = info->vq;
    + vp_dev = to_vp_device(vq->vdev);
    + ioaddr = vp_dev->ioaddr;
    + index = vq->index;
    +
    + if (vp_dev->msix_enabled)
    + writew(VIRTIO_MSI_NO_VECTOR, ioaddr + QUEUE_MSIX_VECTOR(index));
    +
    + writew(QUEUE_CMD_DEACTIVATE, ioaddr + QUEUE_CMD(index));
    + vring_del_virtqueue(vq);
    +}
    +
    +static const struct virtio_config_ops virtio_pci_epf_config_ops = {
    + .get = virtio_pci_epf_get,
    + .set = virtio_pci_epf_set,
    + .get_status = virtio_pci_epf_get_status,
    + .set_status = virtio_pci_epf_set_status,
    + .reset = virtio_pci_epf_reset,
    + .find_vqs = vp_find_vqs,
    + .del_vqs = vp_del_vqs,
    + .get_features = virtio_pci_epf_get_features,
    + .finalize_features = virtio_pci_epf_finalize_features,
    + .bus_name = vp_bus_name,
    + .set_vq_affinity = vp_set_vq_affinity,
    + .get_vq_affinity = vp_get_vq_affinity,
    +};
    +
    +/* virtio_pci_epf_release_dev - Callback function to free device
    + * @dev: Device in virtio_device 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 virtio_pci_epf.
    + */
    +static void virtio_pci_epf_release_dev(struct device *dev)
    +{
    + struct virtio_pci_device *vp_dev;
    + struct virtio_pci_epf *pci_epf;
    + struct virtio_device *vdev;
    +
    + vdev = dev_to_virtio(dev);
    + vp_dev = to_vp_device(vdev);
    + pci_epf = to_virtio_pci_epf(vp_dev);
    +
    + kfree(pci_epf);
    +}
    +
    +/* virtio_pci_epf_probe - Initialize struct virtio_pci_epf when a new PCIe
    + * device is created
    + * @pdev: The pci_dev that is created by the PCIe core during enumeration
    + * @id: pci_device_id of the @pdev
    + *
    + * Probe function to initialize struct virtio_pci_epf when a new PCIe device is
    + * created.
    + */
    +static int virtio_pci_epf_probe(struct pci_dev *pdev,
    + const struct pci_device_id *id)
    +{
    + struct virtio_pci_device *vp_dev, *reg_dev = NULL;
    + struct virtio_pci_epf *pci_epf;
    + struct device *dev;
    + int err;
    +
    + if (pci_is_bridge(pdev))
    + return -ENODEV;
    +
    + pci_epf = kzalloc(sizeof(*pci_epf), GFP_KERNEL);
    + if (!pci_epf)
    + return -ENOMEM;
    +
    + dev = &pdev->dev;
    + vp_dev = &pci_epf->vp_dev;
    + vp_dev->vdev.dev.parent = dev;
    + vp_dev->vdev.dev.release = virtio_pci_epf_release_dev;
    + vp_dev->pci_dev = pdev;
    + INIT_LIST_HEAD(&vp_dev->virtqueues);
    + spin_lock_init(&vp_dev->lock);
    + mutex_init(&pci_epf->lock);
    +
    + err = pci_enable_device(pdev);
    + if (err) {
    + dev_err(dev, "Cannot enable PCI device\n");
    + goto err_enable_device;
    + }
    +
    + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
    + if (err) {
    + dev_err(dev, "Cannot set DMA mask\n");
    + goto err_dma_set_mask;
    + }
    +
    + err = pci_request_regions(pdev, DRV_MODULE_NAME);
    + if (err) {
    + dev_err(dev, "Cannot obtain PCI resources\n");
    + goto err_dma_set_mask;
    + }
    +
    + pci_set_master(pdev);
    +
    + vp_dev->ioaddr = pci_ioremap_bar(pdev, 0);
    + if (!vp_dev->ioaddr) {
    + dev_err(dev, "Failed to read BAR0\n");
    + goto err_ioremap;
    + }
    +
    + pci_set_drvdata(pdev, vp_dev);
    + vp_dev->isr = vp_dev->ioaddr + ISR;
    +
    + /*
    + * we use the subsystem vendor/device id as the virtio vendor/device
    + * id. this allows us to use the same PCI vendor/device id for all
    + * virtio devices and to identify the particular virtio driver by
    + * the subsystem ids
    + */
    + vp_dev->vdev.id.vendor = pdev->subsystem_vendor;
    + vp_dev->vdev.id.device = pdev->subsystem_device;
    +
    + vp_dev->vdev.config = &virtio_pci_epf_config_ops;
    +
    + vp_dev->config_vector = virtio_pci_epf_config_vector;
    + vp_dev->setup_vq = virtio_pci_epf_setup_vq;
    + vp_dev->del_vq = virtio_pci_epf_del_vq;
    +
    + err = register_virtio_device(&vp_dev->vdev);
    + reg_dev = vp_dev;
    + if (err) {
    + dev_err(dev, "Failed to register VIRTIO device\n");
    + goto err_register_virtio;
    + }
    +
    + return 0;
    +
    +err_register_virtio:
    + pci_iounmap(pdev, vp_dev->ioaddr);
    +
    +err_ioremap:
    + pci_release_regions(pdev);
    +
    +err_dma_set_mask:
    + pci_disable_device(pdev);
    +
    +err_enable_device:
    + if (reg_dev)
    + put_device(&vp_dev->vdev.dev);
    + else
    + kfree(vp_dev);
    +
    + return err;
    +}
    +
    +/* virtio_pci_epf_remove - Free the initializations performed by virtio_pci_epf_probe()
    + * @pdev: The pci_dev that is created by the PCIe core during enumeration
    + *
    + * Free the initializations performed by virtio_pci_epf_probe().
    + */
    +void virtio_pci_epf_remove(struct pci_dev *pdev)
    +{
    + struct virtio_pci_device *vp_dev;
    +
    + vp_dev = pci_get_drvdata(pdev);
    +
    + unregister_virtio_device(&vp_dev->vdev);
    + pci_iounmap(pdev, vp_dev->ioaddr);
    + pci_release_regions(pdev);
    + pci_disable_device(pdev);
    +}
    +
    +static const struct pci_device_id virtio_pci_epf_table[] = {
    + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x),
    + },
    + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x),
    + },
    + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
    + },
    + { }
    +};
    +MODULE_DEVICE_TABLE(pci, virtio_pci_epf_table);
    +
    +static struct pci_driver virtio_pci_epf_driver = {
    + .name = DRV_MODULE_NAME,
    + .id_table = virtio_pci_epf_table,
    + .probe = virtio_pci_epf_probe,
    + .remove = virtio_pci_epf_remove,
    + .sriov_configure = pci_sriov_configure_simple,
    +};
    +module_pci_driver(virtio_pci_epf_driver);
    +
    +MODULE_DESCRIPTION("VIRTIO PCI EPF DRIVER");
    +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
    +MODULE_LICENSE("GPL v2");
    diff --git a/include/linux/virtio.h b/include/linux/virtio.h
    index a493eac08393..0eb51b31545d 100644
    --- a/include/linux/virtio.h
    +++ b/include/linux/virtio.h
    @@ -19,6 +19,7 @@
    * @priv: a pointer for the virtqueue implementation to use.
    * @index: the zero-based ordinal number for this queue.
    * @num_free: number of elements we expect to be able to fit.
    + * @mutex: mutex to protect concurrent access to the same queue
    *
    * A note on @num_free: with indirect buffers, each buffer needs one
    * element in the queue, otherwise a buffer will need one element per
    @@ -31,6 +32,8 @@ struct virtqueue {
    struct virtio_device *vdev;
    unsigned int index;
    unsigned int num_free;
    + /* mutex to protect concurrent access to the queue while configuring */
    + struct mutex lock;
    void *priv;
    };

    --
    2.17.1
    \
     
     \ /
      Last update: 2020-07-02 10:24    [W:2.952 / U:0.080 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site