From 8236c0705159bedc7eb82591a257b7c6579721c5 Mon Sep 17 00:00:00 2001 From: Yuanyuan Zhao Date: Fri, 18 Apr 2025 21:11:59 +0800 Subject: [PATCH] vos: add BE and FE status synchronization Add udm synchronization between BE and FE. 1. Add handshake. When the VM which UDM run in shutdown and BE do not have change to notify FE, running FE can know the BE status change by check handshake mask. The handshake mask lays in virtio header in share memory BAR. It is checked every 2 seconds. 2. Add event interrupt. When the backend application exits normally or the VM where the backend resides is shut down normally, BE can trigger a interrupt, and notify FE the event. 3. Create /dev/virtio_shmemX which set auto or manual unresigter virtio device by 'VIRTIO_SHMEM_IOCTL_AUTO_REMOVE'. In this state, APP in user space can unregister the device by 'VIRTIO_SHMEM_IOCTL_UNREGISTER'. Signed-off-by: Yuanyuan Zhao --- drivers/virtio/virtio_guest_shm.c | 10 +- drivers/virtio/virtio_ivshmem.c | 40 +-- drivers/virtio/virtio_shmem.c | 421 ++++++++++++++++++++++++++++-- drivers/virtio/virtio_shmem.h | 33 ++- 4 files changed, 430 insertions(+), 74 deletions(-) diff --git a/drivers/virtio/virtio_guest_shm.c b/drivers/virtio/virtio_guest_shm.c index e23395d6644ee..bcb3fbc4e20d6 100644 --- a/drivers/virtio/virtio_guest_shm.c +++ b/drivers/virtio/virtio_guest_shm.c @@ -130,18 +130,12 @@ static int virtio_ivshmem_probe(struct pci_dev *pci_dev, vi_dev->notify_peer = virtio_guest_shm_notify_peer; vi_dev->early_irq_handler = virtio_guest_shm_early_irq_handler; vi_dev->priv = vi_data; + vi_dev->virtio_registered = false; ret = virtio_shmem_probe(vi_dev); if (ret) goto err_enable; - ret = register_virtio_device(&vi_dev->vdev); - if (ret) { - dev_err(&pci_dev->dev, "failed to register device\n"); - put_device(&vi_dev->vdev.dev); - goto err_enable; - } - #ifdef CONFIG_VIRTIO_IVSHMEM_DEBUG vi_dev->shmem_sz_used = (vi_dev->virtio_header->size + (chunk_size - 1)) & ~(chunk_size - 1); @@ -166,7 +160,7 @@ static void virtio_ivshmem_remove(struct pci_dev *pci_dev) struct virtio_shmem_device *vi_dev = pci_get_drvdata(pci_dev); struct device *dev = get_device(&vi_dev->vdev.dev); - unregister_virtio_device(&vi_dev->vdev); + virtio_shmem_remove(vi_dev); pci_disable_device(pci_dev); put_device(dev); kfree(vi_dev); diff --git a/drivers/virtio/virtio_ivshmem.c b/drivers/virtio/virtio_ivshmem.c index 7bd1ac0608328..8f11b7c8821b5 100644 --- a/drivers/virtio/virtio_ivshmem.c +++ b/drivers/virtio/virtio_ivshmem.c @@ -34,30 +34,6 @@ static void virtio_ivshmem_notify_peer(struct virtio_shmem_device *vi_dev, unsig writel((vi_dev->peer_id << 16), &vi_data->ivshm_regs->doorbell); } -static int virito_ivshmem_check_virtio_supports(struct pci_dev *pdev) -{ - unsigned long bar_start, bar_len; - void __iomem *bar_addr; - int vendor, err = -1; - - bar_start = pci_resource_start(pdev, 2); - bar_len = pci_resource_len(pdev, 2); - if (bar_start && bar_len) { - if (pci_resource_flags(pdev, 2) & IORESOURCE_MEM) { - bar_addr = pci_iomap(pdev, 2, bar_len); - if (!bar_addr) { - dev_err(&pdev->dev, "Could not map memory BAR #%d\n", 2); - return -1; - } - vendor = readw(bar_addr + IVSHMEM_VENDOR_OFFSET); - if (vendor == PCI_VENDOR_ID_REDHAT_QUMRANET) - err = 0; - pci_iounmap(pdev, bar_addr); - } - } - return err; -} - static int virtio_ivshmem_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { @@ -95,12 +71,6 @@ static int virtio_ivshmem_probe(struct pci_dev *pci_dev, vi_dev->this_id = readl(&vi_data->ivshm_regs->ivpos); - ret = virito_ivshmem_check_virtio_supports(pci_dev); - if (ret != 0) { - dev_err(&pci_dev->dev, "not a virtio device\n"); - goto err_enable; - } - section_sz = pci_resource_len(pci_dev, 2); if (section_sz == 0) { dev_err(&pci_dev->dev, "missing shared memory\n"); @@ -118,18 +88,12 @@ static int virtio_ivshmem_probe(struct pci_dev *pci_dev, vi_dev->shmem_sz = section_sz; vi_dev->notify_peer = virtio_ivshmem_notify_peer; vi_dev->priv = vi_data; + vi_dev->virtio_registered = false; ret = virtio_shmem_probe(vi_dev); if (ret) goto err_enable; - ret = register_virtio_device(&vi_dev->vdev); - if (ret) { - dev_err(&pci_dev->dev, "failed to register device\n"); - put_device(&vi_dev->vdev.dev); - goto err_enable; - } - #ifdef CONFIG_VIRTIO_IVSHMEM_DEBUG vi_dev->shmem_sz_used = (vi_dev->virtio_header->size + (chunk_size - 1)) & ~(chunk_size - 1); @@ -154,7 +118,7 @@ static void virtio_ivshmem_remove(struct pci_dev *pci_dev) struct virtio_shmem_device *vi_dev = pci_get_drvdata(pci_dev); struct device *dev = get_device(&vi_dev->vdev.dev); - unregister_virtio_device(&vi_dev->vdev); + virtio_shmem_remove(vi_dev); pci_disable_device(pci_dev); put_device(dev); kfree(vi_dev); diff --git a/drivers/virtio/virtio_shmem.c b/drivers/virtio/virtio_shmem.c index 992b69c4cb681..87d47037f3831 100644 --- a/drivers/virtio/virtio_shmem.c +++ b/drivers/virtio/virtio_shmem.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,14 @@ #define VI_REG_OFFSET(reg) offsetof(struct virtio_shmem_header, reg) #define VI_CFG_REG_OFFSET(reg) VI_REG_OFFSET(common_config.reg) +#define VIRTIO_SHMEM_BE_STATUS_ACTIVE 1 +#define VIRTIO_SHMEM_BE_STATUS_INACTIVE 2 +#define VIRTIO_SHMEM_BE_STATUS_RESET 3 + +#define VIRTIO_SHMEM_HANDSHAKE_MASK 0xa69 +#define VIRTIO_SHMEM_HANDSHAKE_ACK 0x0b69 +#define VIRTIO_SHMEM_SYNC_TIMES 10000 + struct virtio_shmem_vq_info { /* the actual virtqueue */ struct virtqueue *vq; @@ -44,6 +53,22 @@ struct virtio_shmem_vq_info { /* the list node for the virtqueues list */ struct list_head node; }; +#define VIRTIO_SHMEM_NAME "virtio_shmem" +#define VIRTIO_SHMEM_MAX_DEVICES (1U << MINORBITS) +static DEFINE_IDR(virtio_shmem_idr); +static DEFINE_MUTEX(minor_lock); +static int virtio_shmem_major; +static struct cdev *virtio_shmem_cdev; + +static struct attribute *virtio_ivshmem_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(virtio_ivshmem); +static struct class virtio_ivshmem_class = { + .name = "virtio_ivshmem", + .dev_groups = virtio_ivshmem_groups, +}; + static inline unsigned int get_custom_order(unsigned long size, unsigned int shift) @@ -57,17 +82,63 @@ static inline unsigned int get_custom_order(unsigned long size, #endif } +static int virtio_shmem_reset_virtio_dev(struct virtio_shmem_device *vi_dev); +static void virtio_shmem_unregister_virtio_dev(struct virtio_shmem_device *vi_dev, int force); +static int virtio_shmem_register_virtio_dev(struct virtio_shmem_device *vi_dev); + static inline struct virtio_shmem_device * to_virtio_shmem_device(struct virtio_device *vdev) { return container_of(vdev, struct virtio_shmem_device, vdev); } +static int virtio_shmem_be_status(struct virtio_shmem_device *vi_dev) +{ + uint32_t mask = READ_ONCE(vi_dev->virtio_header->handshake); + + if ((mask & 0xffff) != VIRTIO_SHMEM_HANDSHAKE_MASK) + return VIRTIO_SHMEM_BE_STATUS_INACTIVE; + if ((mask & 0xffff0000) >> 16 != vi_dev->backend_rand && vi_dev->virtio_registered == true) + return VIRTIO_SHMEM_BE_STATUS_RESET; + else + return VIRTIO_SHMEM_BE_STATUS_ACTIVE; +} + +static void vi_handshake_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct virtio_shmem_device *vi_dev = + container_of(dwork, struct virtio_shmem_device, shmem_handshake_work); + + switch (virtio_shmem_be_status(vi_dev)) { + case VIRTIO_SHMEM_BE_STATUS_ACTIVE: + if(virtio_shmem_register_virtio_dev(vi_dev)) + put_device(&vi_dev->vdev.dev); + break; + case VIRTIO_SHMEM_BE_STATUS_RESET: + if(virtio_shmem_reset_virtio_dev(vi_dev)) + put_device(&vi_dev->vdev.dev); + break; + default: + virtio_shmem_unregister_virtio_dev(vi_dev, 0); + break; + } + WRITE_ONCE(vi_dev->virtio_header->handshake, (vi_dev->peer_id << 16) | VIRTIO_SHMEM_HANDSHAKE_ACK); + schedule_delayed_work(&vi_dev->shmem_handshake_work, HZ * 2); +} + static bool vi_synchronize_reg_write(struct virtio_shmem_device *vi_dev) { - while (READ_ONCE(vi_dev->virtio_header->write_transaction)) + int times = 0; + while (READ_ONCE(vi_dev->virtio_header->write_transaction)) { cpu_relax(); - + if(times++ > VIRTIO_SHMEM_SYNC_TIMES) { + if (virtio_shmem_be_status(vi_dev) != VIRTIO_SHMEM_BE_STATUS_ACTIVE) + break; + else + times = 0; + } + } return true; } @@ -327,6 +398,17 @@ static irqreturn_t vi_interrupt(int irq, void *opaque) return ret; } +static irqreturn_t vi_event(int irq, void *opaque) +{ + struct virtio_shmem_device *vi_dev = opaque; + + if (vi_dev->virtio_removed == false) { + kobject_uevent(&vi_dev->dev.kobj, KOBJ_UNBIND); + vi_dev->virtio_removed = true; + } + return IRQ_HANDLED; +} + static struct virtqueue *vi_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), @@ -457,12 +539,15 @@ static void vi_del_vqs(struct virtio_device *vdev) free_irq(pci_irq_vector(vi_dev->pci_dev, 0), vi_dev); if (!vi_dev->per_vq_vector && vi_dev->num_vectors > 1) free_irq(pci_irq_vector(vi_dev->pci_dev, 1), vi_dev); + free_irq(pci_irq_vector(vi_dev->pci_dev, READ_ONCE(vi_dev->virtio_header->max_vector) -1), vi_dev); pci_free_irq_vectors(vi_dev->pci_dev); kfree(vi_dev->config_irq_name); vi_dev->config_irq_name = NULL; kfree(vi_dev->queues_irq_name); vi_dev->queues_irq_name = NULL; + kfree(vi_dev->event_irq_name); + vi_dev->event_irq_name = NULL; } static int vi_find_vqs(struct virtio_device *vdev, unsigned int nvqs, @@ -476,7 +561,7 @@ static int vi_find_vqs(struct virtio_device *vdev, unsigned int nvqs, unsigned int vq_vector, desired_vectors; int err, vectors, i, queue_idx = 0; - desired_vectors = 1; /* one for config events */ + desired_vectors = 2; /* one for config events, one for event */ for (i = 0; i < nvqs; i++) if (callbacks[i]) desired_vectors++; @@ -548,8 +633,25 @@ static int vi_find_vqs(struct virtio_device *vdev, unsigned int nvqs, vq_vector++; } + WRITE_ONCE(vi_dev->virtio_header->max_vector, vectors); + vi_dev->event_irq_name = kasprintf(GFP_KERNEL, "%s-event", + dev_name(&vdev->dev)); + if (!vi_dev->event_irq_name) { + err = -ENOMEM; + goto error_event_irq; + } + err = request_irq(pci_irq_vector(vi_dev->pci_dev, desired_vectors - 1), + vi_event, 0, + vi_dev->event_irq_name, vi_dev); + if (err) + goto error_event_irq; + return 0; +error_event_irq: + free_irq(pci_irq_vector(vi_dev->pci_dev, desired_vectors - 1), vi_dev); + kfree(vi_dev->event_irq_name); + vi_dev->event_irq_name = NULL; error_queues_irq: free_irq(pci_irq_vector(vi_dev->pci_dev, 0), vi_dev); kfree(vi_dev->config_irq_name); @@ -559,6 +661,7 @@ static int vi_find_vqs(struct virtio_device *vdev, unsigned int nvqs, kfree(vi_dev->queues_irq_name); vi_dev->queues_irq_name = NULL; pci_free_irq_vectors(vi_dev->pci_dev); + return err; } @@ -596,7 +699,7 @@ static struct page *dma_addr_to_page(struct virtio_shmem_device *vi_dev, dma_add unsigned long pfn; if (dma_handle >= vi_dev->shmem_sz) { - dev_warn(&vi_dev->pci_dev->dev, "DMA handle 0x%llx is out of shared memory region [0x%llx, 0x%llx)\n", + dev_warn(&vi_dev->pci_dev->dev, "DMA handle 0x%llx is out of shared memory region [0x%p, 0x%p)\n", dma_handle, vi_dev->shmem, vi_dev->shmem + vi_dev->shmem_sz); return NULL; } @@ -613,7 +716,7 @@ static dma_addr_t page_to_dma_addr(struct virtio_shmem_device *vi_dev, struct pa pfn = page_to_pfn(page); dma_handle = PFN_PHYS(pfn) - vi_dev->shmem_phys_base; if (dma_handle >= vi_dev->shmem_sz) { - dev_warn(&vi_dev->pci_dev->dev, "PFN 0x%lx is out of shared memory region [0x%llx, 0x%llx)\n", + dev_warn(&vi_dev->pci_dev->dev, "PFN 0x%lx is out of shared memory region [0x%p, 0x%p)\n", pfn, vi_dev->shmem, vi_dev->shmem + vi_dev->shmem_sz); return 0; } @@ -824,7 +927,6 @@ void virtio_shmem_free_page(struct device *dev, struct page *page) void *virtio_shmem_alloc(struct device *dev, size_t size) { - struct pci_dev *pci_dev = to_pci_dev(dev); void *addr; dma_addr_t dma_handle; @@ -869,17 +971,194 @@ static const struct dev_pagemap_ops virtio_shmem_region_pgmap_ops = { .page_free = virtio_shmem_region_page_free, }; +static void virtio_shmem_device_release(struct device *dev) +{ + (void)dev; +} + +static int +vi_register_virtio_dev(struct virtio_shmem_device *vi_dev) +{ + int ret, bitmap_size; + + if (vi_dev->virtio_header->revision < 1 || vi_dev->virtio_header->vendor_id != PCI_VENDOR_ID_REDHAT_QUMRANET) { + dev_err(&vi_dev->pci_dev->dev, "virtio-shmem virtio invalid vendor id 0x%x, version %d\n", + vi_dev->virtio_header->vendor_id, vi_dev->virtio_header->revision); + return -EINVAL; + } + + vi_dev->vdev.dev.parent = &vi_dev->pci_dev->dev; + vi_dev->vdev.dev.release = virtio_shmem_release_dev; + vi_dev->vdev.config = &virtio_shmem_config_ops; + vi_dev->vdev.id.device = vi_dev->virtio_header->device_id; + vi_dev->vdev.id.vendor = vi_dev->virtio_header->vendor_id; + if (vi_dev->virtio_header->backend_flags == 0) { + dev_err(&vi_dev->pci_dev->dev, "backend is not present\n"); + return -EINVAL; + } + vi_dev->peer_id = vi_dev->virtio_header->backend_id; + vi_dev->virtio_header->frontend_status = (vi_dev->this_id << 16) | FRONTEND_FLAG_PRESENT; + + vi_dev->vdev.id.device = vi_dev->virtio_header->device_id; + vi_dev->vdev.id.vendor = vi_dev->virtio_header->vendor_id; + vi_dev->backend_rand = (vi_dev->virtio_header->handshake & 0xffff0000) >> 16; + + + /* mark the header chunks used */ + bitmap_size = BITS_TO_LONGS(vi_dev->shmem_sz >> vi_dev->alloc_shift) * sizeof(long); + memset(vi_dev->alloc_bitmap, 1, bitmap_size); + bitmap_set(vi_dev->alloc_bitmap, 0, + 1 << get_custom_order(vi_dev->virtio_header->size, + vi_dev->alloc_shift)); + ret = register_virtio_device(&vi_dev->vdev); + if (!ret) { + vi_dev->virtio_registered = true; + } + kobject_uevent(&vi_dev->dev.kobj, KOBJ_BIND); + vi_dev->virtio_removed = false; + return ret; +} + +static void +vi_unregister_virtio_dev(struct virtio_shmem_device *vi_dev) +{ + unregister_virtio_device(&vi_dev->vdev); + memset(&vi_dev->vdev, 0, sizeof(struct virtio_device)); + vi_dev->virtio_registered = false; +} + +static int virtio_shmem_register_virtio_dev(struct virtio_shmem_device *vi_dev) +{ + if (vi_dev->virtio_registered == false) { + return vi_register_virtio_dev(vi_dev); + } + return 0; +} + +static void virtio_shmem_unregister_virtio_dev(struct virtio_shmem_device *vi_dev, int force) +{ + if (vi_dev->virtio_registered == true) { + if (vi_dev->virtio_removed == false) { + kobject_uevent(&vi_dev->dev.kobj, KOBJ_UNBIND); + vi_dev->virtio_removed = true; + } + dev_dbg(&vi_dev->pci_dev->dev, "virtio shmem unregister virtio device\n"); + if (vi_dev->auto_unregister == true || force == 1) + vi_unregister_virtio_dev(vi_dev); + } +} + +static int virtio_shmem_reset_virtio_dev(struct virtio_shmem_device *vi_dev) +{ + dev_dbg(&vi_dev->pci_dev->dev, "virtio shmem reset virtio device\n"); + if (vi_dev->virtio_removed == false) { + kobject_uevent(&vi_dev->dev.kobj, KOBJ_UNBIND); + vi_dev->virtio_removed = true; + } + if (vi_dev->virtio_registered == false || vi_dev->auto_unregister == true) { + return 0; + } + virtio_shmem_unregister_virtio_dev(vi_dev, 0); + return virtio_shmem_register_virtio_dev(vi_dev); +} + +static long virtio_shmem_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + int data; + struct virtio_shmem_user *user = filp->private_data; + struct virtio_shmem_device *vi_dev = user->vi_dev; + + switch (cmd) { + case VIRTIO_SHMEM_IOCTL_AUTO_REMOVE: + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + ret = -EFAULT; + } else { + vi_dev->auto_unregister = data; + } + break; + + case VIRTIO_SHMEM_IOCTL_UNREGISTER: + virtio_shmem_unregister_virtio_dev(vi_dev, 1); + break; + + default: + ret = -ENOTTY; + break; + } + return ret; +} + +static int virtio_ivshmem_open(struct inode *inode, struct file *filep) +{ + int err = 0; + struct virtio_shmem_device *vi_dev; + struct virtio_shmem_user *user; + + mutex_lock(&minor_lock); + vi_dev = idr_find(&virtio_shmem_idr, iminor(inode)); + mutex_unlock(&minor_lock); + if (!vi_dev) { + err = -ENODEV; + goto out; + } + + get_device(&vi_dev->dev); + + if (!try_module_get(vi_dev->owner)) { + err = -ENODEV; + goto out_put_device; + } + + user = kmalloc(sizeof(*user), GFP_KERNEL); + if (!user) { + err = -ENOMEM; + goto out_put_module; + } + + user->vi_dev = vi_dev; + filep->private_data = user; + + return 0; + +out_put_module: + module_put(vi_dev->owner); +out_put_device: + put_device(&vi_dev->dev); +out: + return err; +} + +static int virtio_ivshmem_release(struct inode *inode, struct file *filep) +{ + int err = 0; + struct virtio_shmem_user *user = filep->private_data; + struct virtio_shmem_device *vi_dev = user->vi_dev; + + kfree(user); + module_put(vi_dev->owner); + put_device(&vi_dev->dev); + + return err; +} + +static const struct file_operations virtio_ivshmem_fops = { + .owner = THIS_MODULE, + .open = virtio_ivshmem_open, + .release = virtio_ivshmem_release, + .unlocked_ioctl = virtio_shmem_ioctl, +}; + int virtio_shmem_probe(struct virtio_shmem_device *vi_dev) { unsigned int chunks, chunk_size, bitmap_size; struct pci_dev *pci_dev; struct dev_pagemap *pgmap; + int ret = 0; pci_dev = vi_dev->pci_dev; - vi_dev->vdev.dev.release = virtio_shmem_release_dev; - vi_dev->vdev.config = &virtio_shmem_config_ops; - spin_lock_init(&vi_dev->virtqueues_lock); INIT_LIST_HEAD(&vi_dev->virtqueues); @@ -901,19 +1180,7 @@ int virtio_shmem_probe(struct virtio_shmem_device *vi_dev) return -ENOMEM; vi_dev->virtio_header = vi_dev->shmem; - if (vi_dev->virtio_header->revision < 1) { - dev_err(&pci_dev->dev, "invalid virtio-shmem revision\n"); - return -EINVAL; - } - if (vi_dev->virtio_header->backend_flags == 0) { - dev_err(&pci_dev->dev, "backend is not present\n"); - return -EINVAL; - } - vi_dev->peer_id = vi_dev->virtio_header->backend_id; - vi_dev->virtio_header->frontend_status = (vi_dev->this_id << 16) | FRONTEND_FLAG_PRESENT; - - vi_dev->vdev.id.device = vi_dev->virtio_header->device_id; - vi_dev->vdev.id.vendor = vi_dev->virtio_header->vendor_id; + vi_dev->virtio_header->handshake = (vi_dev->this_id << 16) | VIRTIO_SHMEM_HANDSHAKE_ACK; spin_lock_init(&vi_dev->alloc_lock); @@ -932,11 +1199,6 @@ int virtio_shmem_probe(struct virtio_shmem_device *vi_dev) if (!vi_dev->alloc_bitmap) return -ENOMEM; - /* mark the header chunks used */ - bitmap_set(vi_dev->alloc_bitmap, 0, - 1 << get_custom_order(vi_dev->virtio_header->size, - vi_dev->alloc_shift)); - vi_dev->map_src_addr = devm_kzalloc(&pci_dev->dev, chunks * sizeof(void *), GFP_KERNEL); @@ -945,9 +1207,114 @@ int virtio_shmem_probe(struct virtio_shmem_device *vi_dev) set_dma_ops(&pci_dev->dev, &virtio_shmem_dma_ops); + vi_dev->auto_unregister = 1; + + // /dev/virtio_shmemX + vi_dev->owner = THIS_MODULE; + mutex_lock(&minor_lock); + vi_dev->minor = idr_alloc(&virtio_shmem_idr, vi_dev, 0, VIRTIO_SHMEM_MAX_DEVICES, GFP_KERNEL); + mutex_unlock(&minor_lock); + device_initialize(&vi_dev->dev); + vi_dev->dev.devt = MKDEV(virtio_shmem_major, vi_dev->minor); + vi_dev->dev.parent = &vi_dev->pci_dev->dev; + vi_dev->dev.class = &virtio_ivshmem_class; + vi_dev->dev.release = virtio_shmem_device_release; + dev_set_drvdata(&vi_dev->dev, vi_dev); + + ret = dev_set_name(&vi_dev->dev, "virtio_shmem%d", vi_dev->minor); + if (ret) + goto err_device_create; + + ret = device_add(&vi_dev->dev); + if (ret) + goto err_device_create; + + INIT_DELAYED_WORK(&vi_dev->shmem_handshake_work, vi_handshake_work); + schedule_delayed_work(&vi_dev->shmem_handshake_work, 2 * HZ); + if (virtio_shmem_be_status(vi_dev) == VIRTIO_SHMEM_BE_STATUS_ACTIVE) { + if(virtio_shmem_register_virtio_dev(vi_dev)) { + put_device(&vi_dev->vdev.dev); + return -EINVAL; + } + } + return 0; +err_device_create: + unregister_virtio_device(&vi_dev->vdev); + vi_dev->virtio_registered = false; + vi_dev->backend_rand = 0; + + mutex_lock(&minor_lock); + idr_remove(&virtio_shmem_idr, vi_dev->minor); + mutex_unlock(&minor_lock); + + put_device(&vi_dev->dev); + return ret; +} + +void virtio_shmem_remove(struct virtio_shmem_device *vi_dev) +{ + mutex_lock(&minor_lock); + idr_remove(&virtio_shmem_idr, vi_dev->minor); + mutex_unlock(&minor_lock); + device_del(&vi_dev->dev); + + virtio_shmem_unregister_virtio_dev(vi_dev, 1); +} + +static int __init virito_shmem_init(void) +{ + struct cdev *cdev = NULL; + dev_t virtio_shmem_dev = 0; + int result; + + result = alloc_chrdev_region(&virtio_shmem_dev, 0, VIRTIO_SHMEM_MAX_DEVICES, VIRTIO_SHMEM_NAME); + if (result) + return result; + + result = -ENOMEM; + cdev = cdev_alloc(); + if (!cdev) + goto err_cdev_alloc; + + cdev->owner = THIS_MODULE; + cdev->ops = &virtio_ivshmem_fops; + kobject_set_name(&cdev->kobj, "%s", VIRTIO_SHMEM_NAME); + + result = cdev_add(cdev, virtio_shmem_dev, VIRTIO_SHMEM_MAX_DEVICES); + if (result) + goto err_cdev_add; + + virtio_shmem_major = MAJOR(virtio_shmem_dev); + virtio_shmem_cdev = cdev; + + result = class_register(&virtio_ivshmem_class); + if (result) { + goto err_class_register; + } + + return 0; +err_class_register: + class_unregister(&virtio_ivshmem_class); +err_cdev_add: + kobject_put(&cdev->kobj); + cdev_del(cdev); +err_cdev_alloc: + unregister_chrdev_region(virtio_shmem_dev, VIRTIO_SHMEM_MAX_DEVICES); + return result; + +} + +static void __exit virtio_ivshmem_exit(void) +{ + class_unregister(&virtio_ivshmem_class); + unregister_chrdev_region(MKDEV(virtio_shmem_major, 0), VIRTIO_SHMEM_MAX_DEVICES); + cdev_del(virtio_shmem_cdev); + idr_destroy(&virtio_shmem_idr); } +module_init(virito_shmem_init) +module_exit(virtio_ivshmem_exit) MODULE_AUTHOR("Jan Kiszka "); MODULE_DESCRIPTION("Driver for shared memory based virtio front-end devices"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/virtio/virtio_shmem.h b/drivers/virtio/virtio_shmem.h index 1d58185d10c42..73401f5b741a8 100644 --- a/drivers/virtio/virtio_shmem.h +++ b/drivers/virtio/virtio_shmem.h @@ -7,6 +7,13 @@ #include #include +#define VIRTIO_SHMEM_IOCTL_TYPE 0xA3 + +#define VIRTIO_SHMEM_IOCTL_AUTO_REMOVE \ + _IOW(VIRTIO_SHMEM_IOCTL_TYPE, 0x1, int) +#define VIRTIO_SHMEM_IOCTL_UNREGISTER \ + _IO(VIRTIO_SHMEM_IOCTL_TYPE, 0x2) + struct virtio_shmem_header { __le32 revision; __le32 size; @@ -22,7 +29,8 @@ struct virtio_shmem_header { }; __u8 config_event; __u8 queue_event; - __u8 __rsvd[2]; + __u8 max_vector; + __u8 __rsvd[1]; union { __le32 frontend_status; struct { @@ -37,12 +45,21 @@ struct virtio_shmem_header { __le16 backend_id; }; }; + union { + __le32 handshake; + struct { + __le16 handshake_mask; + __le16 backend_rand; + }; + }; struct virtio_pci_common_cfg common_config; __u8 config[]; }; struct virtio_shmem_device { + struct module *owner; + struct device dev; struct virtio_device vdev; struct pci_dev *pci_dev; @@ -50,6 +67,7 @@ struct virtio_shmem_device { bool per_vq_vector; char *config_irq_name; char *queues_irq_name; + char *event_irq_name; u32 this_id; u32 peer_id; @@ -71,6 +89,14 @@ struct virtio_shmem_device { void (*notify_peer)(struct virtio_shmem_device *vi_dev, unsigned int vector); irqreturn_t (*early_irq_handler)(struct virtio_shmem_device *vi_dev); + bool virtio_registered; + bool virtio_removed; + unsigned short backend_rand; + int auto_unregister; + struct delayed_work shmem_handshake_work; + + int minor; + void *priv; #ifdef CONFIG_VIRTIO_IVSHMEM_DEBUG resource_size_t shmem_sz_used; @@ -82,6 +108,11 @@ struct virtio_shmem_device { #endif }; +struct virtio_shmem_user { + struct virtio_shmem_device *vi_dev; +}; + int virtio_shmem_probe(struct virtio_shmem_device *vi_dev); +void virtio_shmem_remove(struct virtio_shmem_device *vi_dev); #endif /* _DRIVERS_VIRTIO_VIRTIO_SHMEM_H */