diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-11-14 10:59:13 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-11-14 10:59:13 -0800 |
| commit | 23c62304dde29abe92013d85910d03a77dada35d (patch) | |
| tree | 78c11a69c8b8226b0cefc40082c0cdc8030b2f8b /drivers/xen | |
| parent | 54975c6523dc77862c25deeb5fa395990b8d3fc4 (diff) | |
| parent | 2b66f2cc753243a6adb3e9a5c038b0fc59ba7ec9 (diff) | |
| download | lwn-master.tar.gz lwn-master.zip | |
Merge tag 'for-linus-7.4a-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tipHEADmaster
Pull xen fixes from Juergen Gross:
"A bunch of isolated fixes of Xen related drivers"
* tag 'for-linus-7.4a-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip:
Xen/gnttab: improve error handling in gnttab_dma_{alloc,free}_pages()
xen: don't allow gnttab_dma_[alloc|free]_pages() in PV guest
xen/pvcalls: destroy the per-socket workqueue on release
xen/pvcalls: unwind pvcalls_new_active_socket() step by step
xen/privcmd: fix double free when an irqfd is shut down twice
xen/gntdev: fix deadlock between map ioctl and mmap
xen/platform-pci: Disable device before releasing PCI regions
xen/grant-table: annotate on-stack work in gnttab_unmap_refs_sync()
Diffstat (limited to 'drivers/xen')
| -rw-r--r-- | drivers/xen/gntdev.c | 8 | ||||
| -rw-r--r-- | drivers/xen/grant-table.c | 48 | ||||
| -rw-r--r-- | drivers/xen/platform-pci.c | 14 | ||||
| -rw-r--r-- | drivers/xen/privcmd.c | 15 | ||||
| -rw-r--r-- | drivers/xen/pvcalls-back.c | 26 |
5 files changed, 85 insertions, 26 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 49957e3ab49d..385ba47d093b 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -670,15 +670,19 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, mutex_lock(&priv->lock); gntdev_add_map(priv, map); op.index = map->index << PAGE_SHIFT; + mutex_unlock(&priv->lock); if (copy_to_user(u, &op, sizeof(op)) != 0) { - list_del(&map->next); + mutex_lock(&priv->lock); + map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, + op.count); + if (map) + list_del(&map->next); mutex_unlock(&priv->lock); gntdev_put_map(priv, map); return -EFAULT; } - mutex_unlock(&priv->lock); return 0; } diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 604c4eea5930..957500dd309a 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -38,6 +38,7 @@ #include <linux/sched.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/sched/task_stack.h> #include <linux/vmalloc.h> #include <linux/uaccess.h> #include <linux/io.h> @@ -1063,6 +1064,9 @@ int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args) size_t size; int i, ret; + if (WARN_ON(xen_pv_domain())) + return -EINVAL; + if (args->nr_pages < 0 || args->nr_pages > (INT_MAX >> PAGE_SHIFT)) return -ENOMEM; @@ -1090,11 +1094,27 @@ int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args) xenmem_reservation_scrub_page(page); } - xenmem_reservation_va_mapping_reset(args->nr_pages, args->pages); - ret = xenmem_reservation_decrease(args->nr_pages, args->frames); if (ret != args->nr_pages) { pr_debug("Failed to decrease reservation for DMA buffer\n"); + if (ret >= 0) { + /* Free the part where decrease didn't work. */ + size_t done = ret << PAGE_SHIFT; + + if (args->coherent) + dma_free_coherent(args->dev, size - done, + args->vaddr + done, + args->dev_bus_addr + done); + else + dma_free_wc(args->dev, size - done, + args->vaddr + done, + args->dev_bus_addr + done); + + if (!ret) /* Nothing else to do? */ + return -EFAULT; + + args->nr_pages = ret; + } ret = -EFAULT; goto fail; } @@ -1120,6 +1140,9 @@ int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args) size_t size; int i, ret; + if (WARN_ON(xen_pv_domain())) + return -EINVAL; + gnttab_pages_clear_private(args->nr_pages, args->pages); for (i = 0; i < args->nr_pages; i++) @@ -1128,14 +1151,16 @@ int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args) ret = xenmem_reservation_increase(args->nr_pages, args->frames); if (ret != args->nr_pages) { pr_debug("Failed to increase reservation for DMA buffer\n"); - ret = -EFAULT; + /* Can only free what has been re-filled. */ + if (!ret) + return -ENOMEM; + if (ret > 0) + args->nr_pages = ret; + ret = -ENOMEM; } else { ret = 0; } - xenmem_reservation_va_mapping_update(args->nr_pages, args->pages, - args->frames); - size = args->nr_pages << PAGE_SHIFT; if (args->coherent) dma_free_coherent(args->dev, size, @@ -1360,12 +1385,21 @@ static void unmap_refs_callback(int result, int gnttab_unmap_refs_sync(struct gntab_unmap_queue_data *item) { struct unmap_refs_callback_data data; + bool on_stack = object_is_on_stack(item); init_completion(&data.completion); item->data = &data; item->done = &unmap_refs_callback; - gnttab_unmap_refs_async(item); + /* blkback keeps @item, and so its retry work, on the stack. */ + if (on_stack) + INIT_DELAYED_WORK_ONSTACK(&item->gnttab_work, gnttab_unmap_work); + else + INIT_DELAYED_WORK(&item->gnttab_work, gnttab_unmap_work); + item->age = 0; + __gnttab_unmap_refs_async(item); wait_for_completion(&data.completion); + if (on_stack) + destroy_delayed_work_on_stack(&item->gnttab_work); return data.result; } diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c index f2438232518c..a12ac57274a6 100644 --- a/drivers/xen/platform-pci.c +++ b/drivers/xen/platform-pci.c @@ -95,6 +95,7 @@ static int platform_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int i, ret; + int requested_bars = 0; long ioaddr; long mmio_addr, mmio_len; unsigned int max_nr_gframes; @@ -115,16 +116,18 @@ static int platform_pci_probe(struct pci_dev *pdev, if (mmio_addr == 0 || ioaddr == 0) { dev_err(&pdev->dev, "no resources found\n"); ret = -ENOENT; - goto pci_out; + goto out; } ret = pci_request_region(pdev, 1, DRV_NAME); if (ret < 0) - goto pci_out; + goto out; + requested_bars |= BIT(1); ret = pci_request_region(pdev, 0, DRV_NAME); if (ret < 0) - goto mem_out; + goto out; + requested_bars |= BIT(0); platform_mmio = mmio_addr; platform_mmiolen = mmio_len; @@ -165,11 +168,8 @@ irq_out: if (!xen_have_vector_callback) free_irq(pdev->irq, pdev); out: - pci_release_region(pdev, 0); -mem_out: - pci_release_region(pdev, 1); -pci_out: pci_disable_device(pdev); + pci_release_selected_regions(pdev, requested_bars); return ret; } diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 5e56fbc93447..221b31611343 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -901,10 +901,25 @@ struct privcmd_kernel_irqfd { poll_table pt; }; +static bool irqfd_is_active(struct privcmd_kernel_irqfd *kirqfd) +{ + lockdep_assert_held(&irqfds_lock); + + return !list_empty(&kirqfd->list); +} + static void irqfd_deactivate(struct privcmd_kernel_irqfd *kirqfd) { lockdep_assert_held(&irqfds_lock); + /* + * Both the deassign ioctl and the eventfd wakeup path can reach + * this function. list_del_init() marks the irqfd inactive, so + * only the first caller may queue the shutdown work. + */ + if (!irqfd_is_active(kirqfd)) + return; + list_del_init(&kirqfd->list); queue_work(irqfd_cleanup_wq, &kirqfd->shutdown); } diff --git a/drivers/xen/pvcalls-back.c b/drivers/xen/pvcalls-back.c index ba62651a7f33..d97c5eca5f35 100644 --- a/drivers/xen/pvcalls-back.c +++ b/drivers/xen/pvcalls-back.c @@ -337,7 +337,7 @@ static struct sock_mapping *pvcalls_new_active_socket( ret = xenbus_map_ring_valloc(fedata->dev, &ref, 1, &page); if (ret < 0) - goto out; + goto out_map; map->ring = page; map->ring_order = map->ring->ring_order; /* first read the order, then map the data ring */ @@ -345,19 +345,19 @@ static struct sock_mapping *pvcalls_new_active_socket( if (map->ring_order > MAX_RING_ORDER) { pr_warn("%s frontend requested ring_order %u, which is > MAX (%u)\n", __func__, map->ring_order, MAX_RING_ORDER); - goto out; + goto out_unmap_ring; } ret = xenbus_map_ring_valloc(fedata->dev, map->ring->ref, (1 << map->ring_order), &page); if (ret < 0) - goto out; + goto out_unmap_ring; map->bytes = page; ret = bind_interdomain_evtchn_to_irqhandler_lateeoi( fedata->dev, evtchn, pvcalls_back_conn_event, 0, "pvcalls-backend", map); if (ret < 0) - goto out; + goto out_unmap_bytes; map->irq = ret; map->data.in = map->bytes; @@ -365,7 +365,7 @@ static struct sock_mapping *pvcalls_new_active_socket( map->ioworker.wq = alloc_ordered_workqueue("pvcalls_io", 0); if (!map->ioworker.wq) - goto out; + goto out_unbind; atomic_set(&map->io, 1); INIT_WORK(&map->ioworker.register_work, pvcalls_back_ioworker); @@ -381,11 +381,16 @@ static struct sock_mapping *pvcalls_new_active_socket( write_unlock_bh(&map->sock->sk->sk_callback_lock); return map; -out: - down(&fedata->socket_lock); - list_del(&map->list); - pvcalls_back_release_active(fedata->dev, fedata, map); - up(&fedata->socket_lock); + +out_unbind: + unbind_from_irqhandler(map->irq, map); +out_unmap_bytes: + xenbus_unmap_ring_vfree(fedata->dev, map->bytes); +out_unmap_ring: + xenbus_unmap_ring_vfree(fedata->dev, (void *)map->ring); +out_map: + sock_release(sock); + kfree(map); return NULL; } @@ -447,6 +452,7 @@ static int pvcalls_back_release_active(struct xenbus_device *dev, atomic_set(&map->release, 1); flush_work(&map->ioworker.register_work); + destroy_workqueue(map->ioworker.wq); xenbus_unmap_ring_vfree(dev, map->bytes); xenbus_unmap_ring_vfree(dev, (void *)map->ring); |
