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 77ce63a57070e7d3c0762742bac09cb877527948..1c70a31e7c5bf7f74c9b1651235bb2d87ea301dc 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 a2e680f7d39f25af3bcfc09bfaac464de7faec66..6d00af25ec6b04167da503e532f0cbc8792e9bfb 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 1802cf4ef5a5a8ef7a16840879f57a806181e67d..2fc0b038ff8a2b5b16924c931f163c36551801da 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 a47ca5376d9d232a9cf870e6ce9bfc6af5bcefe1..efa8b730409051346b8fb8b845a7587af5066839 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 e4b9b2ce9abf43ed467580c9fe2e71dad9ddf0ef..bd3059adc2fbfd93a913d07adc3b71a6f74f3999 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 3b48640f89d5f1ef00bb4587970c099327a7b039..7e07b321e847863f410b24a982ed55f698380fa1 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 aa6ba9d61e9b10cbbb3ae46a2093b1296baf8f4a..47a02e60898defc8a0dcc1194ef7279d41bd3ea4 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 a6c4bb6c2c012df208eab83d43cfb78a16bd63fe..f76b8da28d20bf6a361156269d0b820ed83c86c8 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 d64ef6d0d4631f4c1358b77c0c2e29546c49e87d..50f11b7b47744e9bfed119a4c51391a65c2a1cc5 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 42c6205e2dc43d725af10a5e0d90614aaaea62d7..b6fa6301c75b0f27a7ec0af9643651d7f9afd572 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 7cd1a10c6244ceb2b78d26b87ea1224c1631f14f..7c04a0fd3a279b4eb24a7cd39eb374c2db7684e4 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 3577118bb4a5876e66f33da5b2bf47fc7b6404f9..b94c72f56617083733a78b8cc96487d1a86d8f37 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 d1cffc2a7f212662c14b062ec44493dc7a70e634..5085a50c3b8c8b450e4a41ea4f81440fa2bfcf9a 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;