lguest: Make sure interrupt is allocated ok by lguest_setup_irq
Stratos Psomadakis [Thu, 12 Jan 2012 05:14:47 +0000 (15:14 +1030)]
Make sure the interrupt is allocated correctly by lguest_setup_irq (check the
return value of irq_alloc_desc_at for -ENOMEM)

Signed-off-by: Stratos Psomadakis <psomas@cslab.ece.ntua.gr>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (cleanups and commentry)

arch/x86/lguest/boot.c
drivers/lguest/lguest_device.c

index cf4603b..642d880 100644 (file)
@@ -856,18 +856,23 @@ static void __init lguest_init_IRQ(void)
 }
 
 /*
- * With CONFIG_SPARSE_IRQ, interrupt descriptors are allocated as-needed, so
- * rather than set them in lguest_init_IRQ we are called here every time an
- * lguest device needs an interrupt.
- *
- * FIXME: irq_alloc_desc_at() can fail due to lack of memory, we should
- * pass that up!
+ * Interrupt descriptors are allocated as-needed, but low-numbered ones are
+ * reserved by the generic x86 code.  So we ignore irq_alloc_desc_at if it
+ * tells us the irq is already used: other errors (ie. ENOMEM) we take
+ * seriously.
  */
-void lguest_setup_irq(unsigned int irq)
+int lguest_setup_irq(unsigned int irq)
 {
-       irq_alloc_desc_at(irq, 0);
+       int err;
+
+       /* Returns -ve error or vector number. */
+       err = irq_alloc_desc_at(irq, 0);
+       if (err < 0 && err != -EEXIST)
+               return err;
+
        irq_set_chip_and_handler_name(irq, &lguest_irq_controller,
                                      handle_level_irq, "level");
+       return 0;
 }
 
 /*
index 6a1d644..9e8388e 100644 (file)
@@ -241,7 +241,7 @@ static void lg_notify(struct virtqueue *vq)
 }
 
 /* An extern declaration inside a C file is bad form.  Don't do it. */
-extern void lguest_setup_irq(unsigned int irq);
+extern int lguest_setup_irq(unsigned int irq);
 
 /*
  * This routine finds the Nth virtqueue described in the configuration of
@@ -304,7 +304,9 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
        }
 
        /* Make sure the interrupt is allocated. */
-       lguest_setup_irq(lvq->config.irq);
+       err = lguest_setup_irq(lvq->config.irq);
+       if (err)
+               goto destroy_vring;
 
        /*
         * Tell the interrupt for this virtqueue to go to the virtio_ring
@@ -317,7 +319,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
        err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,
                          dev_name(&vdev->dev), vq);
        if (err)
-               goto destroy_vring;
+               goto free_desc;
 
        /*
         * Last of all we hook up our 'struct lguest_vq_info" to the
@@ -326,6 +328,8 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
        vq->priv = lvq;
        return vq;
 
+free_desc:
+       irq_free_desc(lvq->config.irq);
 destroy_vring:
        vring_del_virtqueue(vq);
 unmap: