ia64/xen-unstable

changeset 9848:e0f2ec5e8358

Some of the Linux PCI functions called by the virtual configuration
space handlers were making calls into ACPI code which uses semaphores.
Since semaphores can not be locked while atomic (because they could
sleep), I changed the way the PCI backend responds to requests from the
frontend. Previously, the virtual configuration space handlers ran in
the same context as the event channel interrupt handler (which was often
atomic if not always atomic). Now the interrupt handler schedules a
callback function (a bottom half) in the system work queue (keventd)
that will get called in process context at a slightly later time. This
allows the handlers in the virtual configuration space to run in process
context and to call any core PCI function regardless of whether it will
sleep or not.

Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil>
author kaf24@firebug.cl.cam.ac.uk
date Tue Apr 25 18:13:39 2006 +0100 (2006-04-25)
parents 7e8bb50fa8fa
children 1ad06bd6832d
files linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c
line diff
     1.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h	Tue Apr 25 16:08:11 2006 +0100
     1.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h	Tue Apr 25 18:13:39 2006 +0100
     1.3 @@ -11,6 +11,8 @@
     1.4  #include <xen/xenbus.h>
     1.5  #include <linux/list.h>
     1.6  #include <linux/spinlock.h>
     1.7 +#include <linux/workqueue.h>
     1.8 +#include <asm/atomic.h>
     1.9  #include <xen/interface/io/pciif.h>
    1.10  
    1.11  struct pci_dev_entry {
    1.12 @@ -18,6 +20,9 @@ struct pci_dev_entry {
    1.13  	struct pci_dev *dev;
    1.14  };
    1.15  
    1.16 +#define _PDEVF_op_active 	(0)
    1.17 +#define PDEVF_op_active 	(1<<(_PDEVF_op_active))
    1.18 +
    1.19  struct pciback_device {
    1.20  	void *pci_dev_data;
    1.21  	spinlock_t dev_lock;
    1.22 @@ -31,6 +36,10 @@ struct pciback_device {
    1.23  
    1.24  	struct vm_struct *sh_area;
    1.25  	struct xen_pci_sharedinfo *sh_info;
    1.26 +
    1.27 +	unsigned long flags;
    1.28 +
    1.29 +	struct work_struct op_work;
    1.30  };
    1.31  
    1.32  struct pciback_dev_data {
    1.33 @@ -71,6 +80,7 @@ void pciback_release_devices(struct pcib
    1.34  
    1.35  /* Handles events from front-end */
    1.36  irqreturn_t pciback_handle_event(int irq, void *dev_id, struct pt_regs *regs);
    1.37 +void pciback_do_op(void *data);
    1.38  
    1.39  int pciback_xenbus_register(void);
    1.40  void pciback_xenbus_unregister(void);
     2.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c	Tue Apr 25 16:08:11 2006 +0100
     2.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c	Tue Apr 25 18:13:39 2006 +0100
     2.3 @@ -40,18 +40,25 @@ void pciback_reset_device(struct pci_dev
     2.4  	pciback_config_reset(dev);
     2.5  }
     2.6  
     2.7 -irqreturn_t pciback_handle_event(int irq, void *dev_id, struct pt_regs *regs)
     2.8 +static inline void test_and_schedule_op(struct pciback_device *pdev)
     2.9  {
    2.10 -	struct pciback_device *pdev = dev_id;
    2.11 +	/* Check that frontend is requesting an operation and that we are not
    2.12 +	 * already processing a request */
    2.13 +	if (test_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags)
    2.14 +	    && !test_and_set_bit(_PDEVF_op_active, &pdev->flags))
    2.15 +		schedule_work(&pdev->op_work);
    2.16 +}
    2.17 +
    2.18 +/* Performing the configuration space reads/writes must not be done in atomic
    2.19 + * context because some of the pci_* functions can sleep (mostly due to ACPI
    2.20 + * use of semaphores). This function is intended to be called from a work
    2.21 + * queue in process context taking a struct pciback_device as a parameter */
    2.22 +void pciback_do_op(void *data)
    2.23 +{
    2.24 +	struct pciback_device *pdev = data;
    2.25  	struct pci_dev *dev;
    2.26  	struct xen_pci_op *op = &pdev->sh_info->op;
    2.27  
    2.28 -	if (unlikely(!test_bit(_XEN_PCIF_active,
    2.29 -			       (unsigned long *)&pdev->sh_info->flags))) {
    2.30 -		pr_debug("pciback: interrupt, but no active operation\n");
    2.31 -		goto out;
    2.32 -	}
    2.33 -
    2.34  	dev = pciback_get_pci_dev(pdev, op->domain, op->bus, op->devfn);
    2.35  
    2.36  	if (dev == NULL)
    2.37 @@ -65,10 +72,25 @@ irqreturn_t pciback_handle_event(int irq
    2.38  	else
    2.39  		op->err = XEN_PCI_ERR_not_implemented;
    2.40  
    2.41 +	/* Tell the driver domain that we're done. */ 
    2.42  	wmb();
    2.43  	clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
    2.44  	notify_remote_via_irq(pdev->evtchn_irq);
    2.45  
    2.46 -      out:
    2.47 +	/* Mark that we're done */
    2.48 +	wmb();
    2.49 +	clear_bit(_PDEVF_op_active, &pdev->flags);
    2.50 +
    2.51 +	/* Check to see if the driver domain tried to start another request
    2.52 +	 * in between clearing _XEN_PCIF_active and clearing _PDEVF_op_active */
    2.53 +	test_and_schedule_op(pdev);
    2.54 +}
    2.55 +
    2.56 +irqreturn_t pciback_handle_event(int irq, void *dev_id, struct pt_regs *regs)
    2.57 +{
    2.58 +	struct pciback_device *pdev = dev_id;
    2.59 +
    2.60 +	test_and_schedule_op(pdev);
    2.61 +
    2.62  	return IRQ_HANDLED;
    2.63  }
     3.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c	Tue Apr 25 16:08:11 2006 +0100
     3.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c	Tue Apr 25 18:13:39 2006 +0100
     3.3 @@ -31,6 +31,8 @@ static struct pciback_device *alloc_pdev
     3.4  	pdev->evtchn_irq = INVALID_EVTCHN_IRQ;
     3.5  	pdev->be_watching = 0;
     3.6  
     3.7 +	INIT_WORK(&pdev->op_work, pciback_do_op, pdev);
     3.8 +
     3.9  	if (pciback_init_devices(pdev)) {
    3.10  		kfree(pdev);
    3.11  		pdev = NULL;
    3.12 @@ -48,6 +50,11 @@ static void free_pdev(struct pciback_dev
    3.13  	if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ)
    3.14  		unbind_from_irqhandler(pdev->evtchn_irq, pdev);
    3.15  
    3.16 +	/* If the driver domain started an op, make sure we complete it or
    3.17 +	 * delete it before releasing the shared memory */
    3.18 +	cancel_delayed_work(&pdev->op_work);
    3.19 +	flush_scheduled_work();
    3.20 +
    3.21  	if (pdev->sh_info)
    3.22  		xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_area);
    3.23