summaryrefslogtreecommitdiff
path: root/drivers/xen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/gntdev.c8
-rw-r--r--drivers/xen/grant-table.c48
-rw-r--r--drivers/xen/platform-pci.c14
-rw-r--r--drivers/xen/privcmd.c15
-rw-r--r--drivers/xen/pvcalls-back.c26
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);