um: Support dynamic IRQ allocation
authorJohannes Berg <johannes.berg@intel.com>
Wed, 2 Dec 2020 11:59:50 +0000 (12:59 +0100)
committerRichard Weinberger <richard@nod.at>
Sun, 13 Dec 2020 21:22:08 +0000 (22:22 +0100)
It's cumbersome and error-prone to keep adding fixed IRQ numbers,
and for proper device wakeup support for the virtio/vhost-user
support we need to have different IRQs for each device. Even if
in theory two IRQs (with and without wake) might be sufficient,
it's much easier to reason about it when we have dynamic number
assignment. It also makes it easier to add new devices that may
dynamically exist or depending on the configuration, etc.

Add support for this, up to 64 IRQs (the same limit as epoll FDs
we have right now). Since it's not easy to port all the existing
places to dynamic allocation (some data is statically initialized)
keep the low numbers are reserved for the existing hard-coded IRQ
numbers.

Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
13 files changed:
arch/um/drivers/line.c
arch/um/drivers/mconsole_kern.c
arch/um/drivers/net_kern.c
arch/um/drivers/port_kern.c
arch/um/drivers/random.c
arch/um/drivers/ubd_kern.c
arch/um/drivers/vector_kern.c
arch/um/drivers/virtio_uml.c
arch/um/drivers/xterm_kern.c
arch/um/include/asm/irq.h
arch/um/include/shared/irq_kern.h
arch/um/kernel/irq.c
arch/um/kernel/sigio.c

index 77ce63a..1c70a31 100644 (file)
@@ -262,19 +262,25 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
 int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
 {
        const struct line_driver *driver = line->driver;
-       int err = 0;
+       int err;
 
-       if (input)
+       if (input) {
                err = um_request_irq(driver->read_irq, fd, IRQ_READ,
                                     line_interrupt, IRQF_SHARED,
                                     driver->read_irq_name, data);
-       if (err)
-               return err;
-       if (output)
+               if (err < 0)
+                       return err;
+       }
+
+       if (output) {
                err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
                                     line_write_interrupt, IRQF_SHARED,
                                     driver->write_irq_name, data);
-       return err;
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
 }
 
 static int line_activate(struct tty_port *port, struct tty_struct *tty)
index a2e680f..6d00af2 100644 (file)
@@ -738,7 +738,7 @@ static int __init mconsole_init(void)
 
        err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
                             IRQF_SHARED, "mconsole", (void *)sock);
-       if (err) {
+       if (err < 0) {
                printk(KERN_ERR "Failed to get IRQ for management console\n");
                goto out;
        }
index 1802cf4..2fc0b03 100644 (file)
@@ -160,7 +160,7 @@ static int uml_net_open(struct net_device *dev)
 
        err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
                             IRQF_SHARED, dev->name, dev);
-       if (err != 0) {
+       if (err < 0) {
                printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
                err = -ENETUNREACH;
                goto out_close;
index a47ca53..efa8b73 100644 (file)
@@ -100,7 +100,7 @@ static int port_accept(struct port_list *port)
                  .port         = port });
 
        if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
-                         IRQF_SHARED, "telnetd", conn)) {
+                         IRQF_SHARED, "telnetd", conn) < 0) {
                printk(KERN_ERR "port_accept : failed to get IRQ for "
                       "telnetd\n");
                goto out_free;
@@ -182,7 +182,7 @@ void *port_data(int port_num)
        }
 
        if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
-                         IRQF_SHARED, "port", port)) {
+                         IRQF_SHARED, "port", port) < 0) {
                printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
                goto out_close;
        }
index e4b9b2c..bd3059a 100644 (file)
@@ -76,7 +76,7 @@ static int __init rng_init (void)
        random_fd = err;
        err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
                             0, "random", NULL);
-       if (err)
+       if (err < 0)
                goto err_out_cleanup_hw;
 
        sigio_broken(random_fd, 1);
index 3b48640..7e07b32 100644 (file)
@@ -1253,7 +1253,7 @@ static int __init ubd_driver_init(void){
        }
        err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
                             0, "ubd", ubd_devs);
-       if(err != 0)
+       if(err < 0)
                printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
        return 0;
 }
index aa6ba9d..47a02e6 100644 (file)
@@ -1271,7 +1271,7 @@ static int vector_net_open(struct net_device *dev)
                irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd,
                        IRQ_READ, vector_rx_interrupt,
                        IRQF_SHARED, dev->name, dev);
-       if (err != 0) {
+       if (err < 0) {
                netdev_err(dev, "vector_open: failed to get rx irq(%d)\n", err);
                err = -ENETUNREACH;
                goto out_close;
@@ -1286,7 +1286,7 @@ static int vector_net_open(struct net_device *dev)
                        irq_rr + VECTOR_BASE_IRQ, vp->fds->tx_fd,
                                IRQ_WRITE, vector_tx_interrupt,
                                IRQF_SHARED, dev->name, dev);
-               if (err != 0) {
+               if (err < 0) {
                        netdev_err(dev,
                                "vector_open: failed to get tx irq(%d)\n", err);
                        err = -ENETUNREACH;
index a6c4bb6..f76b8da 100644 (file)
@@ -412,7 +412,7 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
        rc = um_request_irq(VIRTIO_IRQ, vu_dev->req_fd, IRQ_READ,
                            vu_req_interrupt, IRQF_SHARED,
                            vu_dev->pdev->name, vu_dev);
-       if (rc)
+       if (rc < 0)
                goto err_close;
 
        rc = vhost_user_send_no_payload_fd(vu_dev, VHOST_USER_SET_SLAVE_REQ_FD,
@@ -854,7 +854,7 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
        info->call_fd = call_fds[0];
        rc = um_request_irq(VIRTIO_IRQ, info->call_fd, IRQ_READ,
                            vu_interrupt, IRQF_SHARED, info->name, vq);
-       if (rc)
+       if (rc < 0)
                goto close_both;
 
        rc = vhost_user_set_vring_call(vu_dev, vq->index, call_fds[1]);
index d64ef6d..50f11b7 100644 (file)
@@ -51,7 +51,7 @@ int xterm_fd(int socket, int *pid_out)
 
        err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt,
                             IRQF_SHARED, "xterm", data);
-       if (err) {
+       if (err < 0) {
                printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
                       "err = %d\n",  err);
                ret = err;
index 42c6205..b6fa630 100644 (file)
 #define VECTOR_BASE_IRQ                (VIRTIO_IRQ + 1)
 #define VECTOR_IRQ_SPACE       8
 
-#define LAST_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ - 1)
+#define UM_FIRST_DYN_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ)
 
 #else
 
-#define LAST_IRQ VIRTIO_IRQ
+#define UM_FIRST_DYN_IRQ (VIRTIO_IRQ + 1)
 
 #endif
 
-#define NR_IRQS (LAST_IRQ + 1)
+#define NR_IRQS                        64
 
 #endif
index 7cd1a10..7c04a0f 100644 (file)
@@ -9,10 +9,10 @@
 #include <linux/interrupt.h>
 #include <asm/ptrace.h>
 
-extern int um_request_irq(unsigned int irq, int fd, int type,
-                         irq_handler_t handler,
-                         unsigned long irqflags,  const char * devname,
-                         void *dev_id);
-void um_free_irq(unsigned int irq, void *dev);
-#endif
+#define UM_IRQ_ALLOC   -1
 
+int um_request_irq(int irq, int fd, int type, irq_handler_t handler,
+                  unsigned long irqflags,  const char * devname,
+                  void *dev_id);
+void um_free_irq(int irq, void *dev_id);
+#endif
index 3577118..b94c72f 100644 (file)
@@ -19,6 +19,7 @@
 #include <kern_util.h>
 #include <os.h>
 #include <irq_user.h>
+#include <irq_kern.h>
 
 
 extern void free_irqs(void);
@@ -38,6 +39,7 @@ struct irq_entry {
 static struct irq_entry *active_fds;
 
 static DEFINE_SPINLOCK(irq_lock);
+static DECLARE_BITMAP(irqs_allocated, NR_IRQS);
 
 static void irq_io_loop(struct irq_fd *irq, struct uml_pt_regs *regs)
 {
@@ -421,27 +423,52 @@ unsigned int do_IRQ(int irq, struct uml_pt_regs *regs)
        return 1;
 }
 
-void um_free_irq(unsigned int irq, void *dev)
+void um_free_irq(int irq, void *dev)
 {
+       if (WARN(irq < 0 || irq > NR_IRQS, "freeing invalid irq %d", irq))
+               return;
+
        free_irq_by_irq_and_dev(irq, dev);
        free_irq(irq, dev);
+       clear_bit(irq, irqs_allocated);
 }
 EXPORT_SYMBOL(um_free_irq);
 
-int um_request_irq(unsigned int irq, int fd, int type,
+int um_request_irq(int irq, int fd, int type,
                   irq_handler_t handler,
                   unsigned long irqflags, const char * devname,
                   void *dev_id)
 {
        int err;
 
+       if (irq == UM_IRQ_ALLOC) {
+               int i;
+
+               for (i = UM_FIRST_DYN_IRQ; i < NR_IRQS; i++) {
+                       if (!test_and_set_bit(i, irqs_allocated)) {
+                               irq = i;
+                               break;
+                       }
+               }
+       }
+
+       if (irq < 0)
+               return -ENOSPC;
+
        if (fd != -1) {
                err = activate_fd(irq, fd, type, dev_id);
                if (err)
-                       return err;
+                       goto error;
        }
 
-       return request_irq(irq, handler, irqflags, devname, dev_id);
+       err = request_irq(irq, handler, irqflags, devname, dev_id);
+       if (err < 0)
+               goto error;
+
+       return irq;
+error:
+       clear_bit(irq, irqs_allocated);
+       return err;
 }
 
 EXPORT_SYMBOL(um_request_irq);
@@ -480,7 +507,7 @@ void __init init_IRQ(void)
        irq_set_chip_and_handler(TIMER_IRQ, &SIGVTALRM_irq_type, handle_edge_irq);
 
 
-       for (i = 1; i <= LAST_IRQ; i++)
+       for (i = 1; i < NR_IRQS; i++)
                irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq);
        /* Initialize EPOLL Loop */
        os_setup_epoll();
index d1cffc2..5085a50 100644 (file)
@@ -25,7 +25,7 @@ int write_sigio_irq(int fd)
 
        err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
                             0, "write sigio", NULL);
-       if (err) {
+       if (err < 0) {
                printk(KERN_ERR "write_sigio_irq : um_request_irq failed, "
                       "err = %d\n", err);
                return -1;