bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH-2] A new irq device interface


From: Junling Ma
Subject: [PATCH-2] A new irq device interface
Date: Sat, 1 Aug 2020 19:01:56 -0700

Hi all,

In this patch, the new interface of irq device is implemented. Please see the 
first patch for a description.

Junling Ma

---
 device/ds_routines.c |   2 +-
 device/intr.c        | 184 +++++++++++++++++++++++++++++++++----------
 device/intr.h        |  11 ++-
 i386/i386at/conf.c   |   4 +-
 4 files changed, 154 insertions(+), 47 deletions(-)

diff --git a/device/ds_routines.c b/device/ds_routines.c
index 91787950..333b66c9 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -345,7 +345,7 @@ ds_device_intr_register (device_t dev, int id,
 
   // TODO detect when the port get destroyed because the driver crashes and
   // restart, to replace it when the same device driver calls it again.
-  err = install_user_intr_handler (id, receive_port);
+  err = install_user_intr_handler (id, receive_port, mdev, FALSE);
   if (err == D_SUCCESS)
     {
       /* If the port is installed successfully, increase its reference by 1.
diff --git a/device/intr.c b/device/intr.c
index 682b12d7..e78f2193 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -22,6 +22,7 @@
 #include <ipc/ipc_space.h>
 #include "io_req.h"
 #include "ds_routines.h"
+#include "device.server.h"
 
 #ifndef MACH_XEN
 
@@ -70,16 +71,20 @@ kern_return_t
 irq_acknowledge (ipc_port_t receive_port)
 {
   user_intr_t *e;
+  user_intr_notification_t n = NULL;
   unsigned id;
        for (id = 0; id < NINTR; ++id)
                {
                        e = irqtab.handler[id];
-                       if (e && search_notification (e, receive_port))
+                       if (e && (n = search_notification (e, receive_port)) != 
NULL)
                                break;
                }
-       if (id == NINTR)
+       if (id == NINTR || n->device_port)
                return D_INVALID_OPERATION;
-
+       /* cannot ack twice */
+       if (n->acked)
+               return D_INVALID_OPERATION;
+       n->acked = TRUE;
   kern_return_t ret = D_SUCCESS;
   PROTECT(e->lock, 
        {
@@ -105,16 +110,7 @@ deliver_user_intr (int id, void *dev_id, struct pt_regs 
*regs)
                        __disable_irq(id);
                }
        });
-       if (!e) return;
-       if (queue_empty(&e->notification_queue))
-               {
-                       irqtab.handler[id] = NULL;
-                       if (e->n_unacked)
-                               __enable_irq (id);
-                       free_irq(id, &irqtab);
-                       e = NULL;
-               }
-       else if (e->interrupts)
+       if (e && !queue_empty(&e->notification_queue) && e->interrupts)
                thread_wakeup ((event_t) &intr_thread);
 }
 
@@ -137,43 +133,65 @@ intr_thread (void)
                                                        user_intr_t *e = 
irqtab.handler[id];
                                                        if (!e) continue;
                                                        clear_wait 
(current_thread (), 0, 0);
-                                                       
user_intr_notification_t next;
-                                                       
user_intr_notification_t n;
-                                                       /* go through each 
notification to fire or remove dead ones */
+                                                       
user_intr_notification_t next, n;
+                                                       /* go through each 
notification to fire interrupts or remove dead ones */
                                                        PROTECT(e->lock,
                                                                {
+                                                                       
boolean_t delivered = FALSE;
                                                                        
queue_iterate (&e->notification_queue, n, user_intr_notification_t, chain)
                                                                                
{
-                                                                               
        /* is the notification port dead? */
-                                                                               
        if (n->dst_port->ip_references == 1) 
+                                                                               
        /* check for dead port */
+                                                                               
        while (n && n->dst_port->ip_references == 1)
                                                                                
                {
                                                                                
                        /* dead, move it to the dead queue */
+                                                                               
                        printf("dead port: %p\n", n->dst_port);
                                                                                
                        next = (user_intr_notification_t)queue_next(&n->chain);
                                                                                
                        queue_remove(&e->notification_queue, n, 
user_intr_notification_t, chain);
-                                                                               
                        ipc_port_release (n->dst_port);
-                                                                               
                        kfree((vm_offset_t)n, sizeof(*n));
-                                                                               
                        --e->n_unacked;
-                                                                               
                        if (e->n_unacked == 0)
-                                                                               
                                __enable_irq(id);
-                                                                               
                        printf("dead notification port %p released \n", 
n->dst_port);
-                                                                               
                        if (queue_empty(&e->notification_queue)) 
+                                                                               
                        //ipc_port_release (n->dst_port);
+                                                                               
                        if (n->request)
+                                                                               
                                io_req_free(n->request);
+                                                                               
                        /* if waiting for acking, ack it */
+                                                                               
                        if (!n->acked && e->n_unacked)
                                                                                
                                {
-                                                                               
                                        printf("irq %d has no registered 
notifications. remove\n", id);
-                                                                               
                                        kfree((vm_offset_t)e, sizeof(*e));
-                                                                               
                                        irqtab.handler[id] = NULL;
-                                                                               
                                        break;
+                                                                               
                                        --e->n_unacked;
+                                                                               
                                        if (e->n_unacked == 0)
+                                                                               
                                                __enable_irq(id);
                                                                                
                                }
+                                                                               
                        kfree((vm_offset_t)n, sizeof(*n));
+                                                                               
                        ds_device_close(&n->device->dev);
                                                                                
                        n = next;
                                                                                
                }
-                                                                               
        else if (e->interrupts)
+                                                                               
        if (!n) break;
+                                                                               
        /* if there is a DEVICE, the notification uses the new interface */
+                                                                               
        /* the notification uses the old interface. is the notification port 
dead? */
+                                                                               
        if (!e->interrupts)
+                                                                               
                continue;
+                                                                               
        if (!n->device_port && !deliver_intr(id, n->dst_port))
+                                                                               
                continue; /* failed to delivery using the notification port */
+                                                                               
        if (n->device_port)
                                                                                
                {
-                                                                               
                        SPLHIGH(e->interrupts--);
-                                                                               
                        total_interrupts += e->interrupts;
-                                                                               
                        if (deliver_intr(id, n->dst_port))
-                                                                               
                                /* n_unacked is increased when firing. Without 
firing, there is no ack */
-                                                                               
                                e->n_unacked++;
+                                                                               
                        if (!n->request)
+                                                                               
                                continue;
+                                                                               
                        ds_read_done(n->request);
+                                                                               
                        io_req_free(n->request);
                                                                                
                }
+                                                                               
        n->request = NULL;
+                                                                               
        e->n_unacked++;
+                                                                               
        n->acked = FALSE;
+                                                                               
        delivered = TRUE;
                                                                                
} /* end of queue_iterate */
+                                                                       if 
(delivered) /* if successfully delivered */
+                                                                               
{
+                                                                               
        SPLHIGH(e->interrupts--);
+                                                                               
        total_interrupts += e->interrupts;
+                                                                               
}
+                                                                       if 
(queue_empty(&e->notification_queue))
+                                                                               
{
+                                                                               
        printf("there is no registered handler for irq %d. close the device\n", 
id);
+                                                                               
        free_irq(id, &irqtab);
+                                                                               
        SPLHIGH(irqtab.handler[id] = NULL);
+                                                                               
        kfree((vm_offset_t)e, sizeof(*e));
+                                                                               
}
                                                                }); /* end of 
PROTECT */
                                                }
                }
@@ -226,9 +244,9 @@ deliver_intr (int id, ipc_port_t dst_port)
 }
 
 int
-install_user_intr_handler (int id, ipc_port_t dst_port)
+install_user_intr_handler (int id, ipc_port_t dst_port, mach_device_t device, 
boolean_t device_port)
 {
-       if (id > NINTR || dst_port == NULL)
+       if (id > NINTR || device == NULL)
                return D_INVALID_OPERATION;
   user_intr_t *e = irqtab.handler[id];
   if (e == NULL) {
@@ -243,7 +261,7 @@ install_user_intr_handler (int id, ipc_port_t dst_port)
                /* install the new handler */
                kern_return_t r = request_irq (id, deliver_user_intr, SA_SHIRQ, 
NULL, &irqtab);
                if (r) {
-                       printf("could not register irq handler: %d(%08x)\n", r, 
r);
+                       printf("could not register irq handler for irq %d: 
%d(%08x)\n", id, r, r);
                        irqtab.handler[id] = NULL;
                        kfree((vm_size_t)e, sizeof(*e));
                        return r;
@@ -253,18 +271,20 @@ install_user_intr_handler (int id, ipc_port_t dst_port)
   /* check whether the intr entry has been in the queue. */
   user_intr_notification_t n = search_notification (e, dst_port);
   if (n)
-    {
-      printf ("the interrupt entry for irq %d and port %p has already been 
inserted\n", id, dst_port);
-      return D_ALREADY_OPEN;
-    }
+    return D_ALREADY_OPEN;
 
        n = (user_intr_notification_t) kalloc (sizeof (*n));
        if (n == NULL)
                return D_NO_MEMORY;
 
   n->dst_port = dst_port;
+  ipc_port_reference(dst_port);
+  n->device = device;
+  n->device_port = device_port;
+  n->request = NULL;
+  n->acked = TRUE;
   PROTECT(e->lock, queue_enter (&e->notification_queue, n, 
user_intr_notification_t, chain));
-  printf("irq handler [%d]: new delivery port %p\n", id, dst_port);
+       printf("notification port %p register for irq %d\n", dst_port, id);
   return D_SUCCESS;
 }
 
@@ -275,4 +295,82 @@ void irq_init()
                irqtab.handler[i] = NULL;
 }
 
+extern int irqopen(dev_t dev, int flag, io_req_t ior)
+{
+       int id = minor(dev);
+       /* for now, we need to handle opening "irq", which is the same as irq0
+          this is for backward compatibility. */
+       if (id == 0)
+               return D_SUCCESS;
+       if (id > NINTR)
+               return D_NO_SUCH_DEVICE;
+  user_intr_t *e = irqtab.handler[id];
+       if (e)
+               return D_SUCCESS;
+       ior->io_error = D_SUCCESS;
+       return install_user_intr_handler (id, ior->io_reply_port, 
ior->io_device, TRUE);
+}
+
+void irqclose(dev_t dev, int flags)
+{
+       /* device closes are handled by port death detection */
+}
+
+extern int irqread(dev_t dev, io_req_t ior)
+{
+       irq_t id = minor(dev);
+       if (id > NINTR)
+               return D_NO_SUCH_DEVICE;
+  user_intr_t *e = irqtab.handler[id];
+  user_intr_notification_t n = NULL;
+  if (e)
+       n = search_notification(e, ior->io_reply_port);
+  if (!n)
+       {
+               int err = install_user_intr_handler(id, ior->io_reply_port, 
ior->io_device, TRUE);
+               if (err)
+                       return err;
+               e = irqtab.handler[id];
+               n = search_notification(e, ior->io_reply_port);
+       }
+  /* already requested */
+  if (n->request || !n->acked)
+       return D_INVALID_OPERATION;
+  n->request = ior;
+       ior->io_error = D_SUCCESS;
+       ior->io_residual = ior->io_count;
+       return D_IO_QUEUED;
+}
+
+extern int irqwrite(dev_t dev, io_req_t ior)
+{
+       irq_t id = minor(dev);
+       if (id > NINTR)
+               return D_NO_SUCH_DEVICE;
+  user_intr_t *e = irqtab.handler[id];
+  if (e == NULL)
+       return D_DEVICE_DOWN;
+  if (e->n_unacked == 0)
+               return D_INVALID_OPERATION;
+       user_intr_notification_t n = search_notification(e, ior->io_reply_port);
+       if (!n || n->request || n->acked)
+               return D_INVALID_OPERATION;
+       n->acked = TRUE;
+       PROTECT(e->lock,
+               {
+                       e->n_unacked--;
+                       if (e->n_unacked == 0) 
+                               __enable_irq(id);
+               });
+       boolean_t wait = FALSE;
+       int rc = device_write_get(ior, &wait);
+       if (rc != KERN_SUCCESS)
+         return rc;
+       if (wait)
+               return D_INVALID_OPERATION;
+       ior->io_residual = ior->io_count;
+       ior->io_error = D_SUCCESS;
+       return D_SUCCESS;
+}
+
 #endif /* MACH_XEN */
diff --git a/device/intr.h b/device/intr.h
index 90bc6f4c..88b40516 100644
--- a/device/intr.h
+++ b/device/intr.h
@@ -33,7 +33,11 @@ struct irqdev;
 /* a struct to hold notifications */
 struct user_intr_notification {
   queue_chain_t chain;
+  io_req_t request;
+  boolean_t acked;
+  mach_device_t device;
   ipc_port_t dst_port; /* Notification port */
+  boolean_t device_port; /* whether dst_port is a device port, i.e, using the 
new interface */
 };
 typedef struct user_intr_notification * user_intr_notification_t;
 
@@ -49,12 +53,17 @@ struct irqdev {
        user_intr_t *handler[NINTR]; /* irq handlers for device_open */
 };
 
-extern int install_user_intr_handler (int id, ipc_port_t dst_port);
+extern int install_user_intr_handler (int id, ipc_port_t dst_port, 
mach_device_t device, boolean_t device_port);
 extern void irq_init();
 
 void intr_thread (void);
 kern_return_t irq_acknowledge (ipc_port_t receive_port);
 
+extern int     irqopen(dev_t dev, int flag, io_req_t ior);
+extern void    irqclose(dev_t dev, int flags);
+extern int     irqread(dev_t dev, io_req_t ior);
+extern int     irqwrite(dev_t dev, io_req_t ior);
+
 #endif /* MACH_XEN */
 
 #endif
diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c
index ca5d0dfb..69f12ae2 100644
--- a/i386/i386at/conf.c
+++ b/i386/i386at/conf.c
@@ -152,8 +152,8 @@ struct dev_ops      dev_name_list[] =
          nodev },
 #endif /* MACH_HYP */
 
-        { irqname,      nulldev_open,   nulldev_close,    nulldev_read,
-          nulldev_write,nulldev_getstat,nulldev_setstat,  nomap,
+        { irqname,      irqopen,   irqclose,    irqread,
+          irqwrite,nulldev_getstat,nulldev_setstat,  nomap,
           nodev,        nulldev,        nulldev_portdeath,0,
           nodev },
 
-- 
2.28.0.rc1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]