bug-hurd
[Top][All Lists]
Advanced

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

[PATCH] Move user_intr handling to mach side


From: Junling Ma
Subject: [PATCH] Move user_intr handling to mach side
Date: Sun, 2 Aug 2020 21:07:00 -0700

1. make deliver_intr a linux irq handler, and do not let it touch the 
main_intr_queue. the queue
   handling is solely in intr_thread (removing dead entries) and 
insert_intr_entry (insertion).
2. remove user_intr from struct linux_action and its handling on linux side.

---
 device/intr.c                    | 156 +++++++++++++------------------
 device/intr.h                    |   1 -
 linux/dev/arch/i386/kernel/irq.c |  73 +--------------
 3 files changed, 65 insertions(+), 165 deletions(-)

diff --git a/device/intr.c b/device/intr.c
index 705dc1c6..3962d7aa 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -23,10 +23,24 @@
 
 #ifndef MACH_XEN
 
+#define SA_SHIRQ 0x04000000
+
 extern struct irqdev irqtab;
 #define main_intr_queue irqtab.intr_queue
 static boolean_t deliver_intr (int id, ipc_port_t dst_port);
 
+struct pt_regs;
+extern int request_irq (unsigned int irq, void (*handler) (int, void *, struct 
pt_regs *),
+            unsigned long flags, const char *device, void *dev_id);
+extern void free_irq (unsigned int irq, void *dev_id);
+
+#define SPLHIGH(critical_section) \
+{\
+  spl_t s = splhigh();\
+  critical_section;\
+  splx(s);\
+}
+
 #define PROTECT(lock, critical_section) \
 {\
   simple_lock(&lock);\
@@ -82,88 +96,52 @@ irq_acknowledge (ipc_port_t receive_port)
   return D_SUCCESS;
 }
 
-/* This function can only be used in the interrupt handler. */
-static void
-queue_intr (struct irqdev *dev, int id, user_intr_t *e)
+void
+deliver_user_intr (int id, void *dev_id, struct pt_regs *regs)
 {
-  /* Until userland has handled the IRQ in the driver, we have to keep it
-   * disabled. Level-triggered interrupts would keep raising otherwise. */
-  __disable_irq (dev->irq[id]);
-
-  spl_t s = splhigh ();
-  e->n_unacked++;
-  e->interrupts++;
-  dev->tot_num_intr++;
-  splx (s);
-
+  SPLHIGH({
+    user_intr_t *e = dev_id;
+    /* Until userland has handled the IRQ in the driver, we have to keep it
+     * disabled. Level-triggered interrupts would keep raising otherwise. */
+    __disable_irq (irqtab.irq[id]);
+    e->n_unacked++;
+    e->interrupts++;
+    irqtab.tot_num_intr++;
+  });
   thread_wakeup ((event_t) &intr_thread);
 }
 
-int
-deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e)
-{
-  /* The reference of the port was increased
-   * when the port was installed.
-   * If the reference is 1, it means the port should
-   * have been destroyed and I destroy it now. */
-  if (e->dst_port
-      && e->dst_port->ip_references == 1)
-    {
-      printf ("irq handler [%d]: release a dead delivery port %p entry %p\n", 
id, e->dst_port, e);
-      ipc_port_release (e->dst_port);
-      e->dst_port = MACH_PORT_NULL;
-      thread_wakeup ((event_t) &intr_thread);
-      return 0;
-    }
-  else
-    {
-      queue_intr (dev, id, e);
-      return 1;
-    }
-}
-
 /* insert an interrupt entry in the queue.
  * This entry exists in the queue until
  * the corresponding interrupt port is removed.*/
 user_intr_t *
 insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port)
 {
-  user_intr_t *e, *new, *ret;
-  int free = 0;
-
-  new = (user_intr_t *) kalloc (sizeof (*new));
-  if (new == NULL)
-    return NULL;
-
   /* check whether the intr entry has been in the queue. */
-  spl_t s = splhigh ();
-  e = search_intr (dev, dst_port);
+  user_intr_t *e = search_intr (dev, dst_port);
   if (e)
     {
       printf ("the interrupt entry for irq[%d] and port %p has already been 
inserted\n", id, dst_port);
-      free = 1;
-      ret = NULL;
-      goto out;
+      return NULL;
     }
-  printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, 
new);
-  ret = new;
-  new->id = id;
-  new->dst_port = dst_port;
-  new->interrupts = 0;
-  new->n_unacked = 0;
-
-  PROTECT(dev->lock, queue_enter (&dev->intr_queue, new, user_intr_t *, 
chain));
-out:
-  splx (s);
-  if (free)
-    kfree ((vm_offset_t) new, sizeof (*new));
-  return ret;
+
+  e = (user_intr_t *) kalloc (sizeof (*e));
+  if (e == NULL)
+    return NULL;
+  e->id = id;
+  e->dst_port = dst_port;
+  e->interrupts = 0;
+  e->n_unacked = 0;
+  /* we do not need to disable irq here, because the irq handler does not 
touch the queue now. */
+  PROTECT(dev->lock, queue_enter (&dev->intr_queue, e, user_intr_t *, chain));
+  printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, e);
+  return e;
 }
 
 void
 intr_thread (void)
 {
-  user_intr_t *e;
+  user_intr_t *e, *next;
   int id;
   ipc_port_t dst_port;
 
@@ -178,8 +156,11 @@ intr_thread (void)
       simple_lock(&irqtab.lock);
       queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
        {
-         if ((!e->dst_port || e->dst_port->ip_references == 1) && e->n_unacked)
+         /* e cannot be NULL, as we check against it when installing the 
handler */
+         assert(e != NULL);
+         while (e->dst_port->ip_references == 1)
            {
+             next = (user_intr_t *)queue_next(&e->chain);
              printf ("irq handler [%d]: release dead delivery %d unacked irqs 
port %p entry %p\n", e->id, e->n_unacked, e->dst_port, e);
              /* The reference of the port was increased
               * when the port was installed.
@@ -193,25 +174,22 @@ intr_thread (void)
                __enable_irq (irqtab.irq[e->id]);
                e->n_unacked--;
              }
+             ipc_port_release(e->dst_port);
+             assert (!queue_empty (&main_intr_queue));
+             queue_remove (&main_intr_queue, e, user_intr_t *, chain);
+             printf("irq handler [%d]: removed entry %p\n", e->id, e);
+             /* if e is the last handler registered for irq ID, then remove 
the linux irq handler */
+             free_irq(e->id, e);
+             kfree ((vm_offset_t) e, sizeof (*e));
+             e = next;
            }
        }
 
       /* Now check for interrupts */
       while (irqtab.tot_num_intr)
        {
-         int del = 0;
-
          queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
            {
-             /* if an entry doesn't have dest port,
-              * we should remove it. */
-             if (e->dst_port == MACH_PORT_NULL)
-               {
-                 clear_wait (current_thread (), 0, 0);
-                 del = 1;
-                 break;
-               }
-
              if (e->interrupts)
                {
                  clear_wait (current_thread (), 0, 0);
@@ -225,24 +203,6 @@ intr_thread (void)
                  s = splhigh ();
                }
            }
-
-         /* remove the entry without dest port from the queue and free it. */
-         if (del)
-           {
-             assert (!queue_empty (&main_intr_queue));
-             queue_remove (&main_intr_queue, e, user_intr_t *, chain);
-             if (e->n_unacked)
-               printf("irq handler [%d]: still %d unacked irqs in entry %p\n", 
e->id, e->n_unacked, e);
-             while (e->n_unacked)
-             {
-               __enable_irq (irqtab.irq[e->id]);
-               e->n_unacked--;
-             }
-             printf("irq handler [%d]: removed entry %p\n", e->id, e);
-             splx (s);
-             kfree ((vm_offset_t) e, sizeof (*e));
-             s = splhigh ();
-           }
        }
       simple_unlock(&irqtab.lock);
       splx (s);
@@ -294,4 +254,16 @@ deliver_intr (int id, ipc_port_t dst_port)
   return TRUE;
 }
 
+int
+install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags,
+                         user_intr_t *user_intr)
+{
+  if (id >= NINTR || user_intr == NULL)
+    return D_INVALID_OPERATION;
+  unsigned int irq = dev->irq[id];
+  if (irq >= NINTR)
+    return D_INVALID_OPERATION;
+  return request_irq (id, deliver_user_intr, SA_SHIRQ, NULL, user_intr);
+}
+
 #endif /* MACH_XEN */
diff --git a/device/intr.h b/device/intr.h
index 54ddb331..b1c09e6c 100644
--- a/device/intr.h
+++ b/device/intr.h
@@ -51,7 +51,6 @@ struct irqdev {
 };
 
 extern int install_user_intr_handler (struct irqdev *dev, int id, unsigned 
long flags, user_intr_t *e);
-extern int deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e);
 extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t 
receive_port);
 
 void intr_thread (void);
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c
index 1e911f33..5879165f 100644
--- a/linux/dev/arch/i386/kernel/irq.c
+++ b/linux/dev/arch/i386/kernel/irq.c
@@ -78,7 +78,6 @@ struct linux_action
   void *dev_id;
   struct linux_action *next;
   unsigned long flags;
-  user_intr_t *user_intr;
 };
 
 static struct linux_action *irq_action[16] =
@@ -98,7 +97,6 @@ linux_intr (int irq)
 {
   struct pt_regs regs;
   struct linux_action *action = *(irq_action + irq);
-  struct linux_action **prev = &irq_action[irq];
   unsigned long flags;
 
   kstat.interrupts[irq]++;
@@ -110,32 +108,11 @@ linux_intr (int irq)
 
   while (action)
     {
-      // TODO I might need to check whether the interrupt belongs to
-      // the current device. But I don't do it for now.
-      if (action->user_intr)
-       {
-         if (!deliver_user_intr(&irqtab, irq, action->user_intr))
-         {
-           *prev = action->next;
-           linux_kfree(action);
-           action = *prev;
-           continue;
-         }
-       }
-      else if (action->handler)
+      if (action->handler)
        action->handler (irq, action->dev_id, &regs);
-      prev = &action->next;
       action = action->next;
     }
 
-  if (!irq_action[irq])
-    {
-      /* No handler any more, disable interrupt */
-      mask_irq (irq);
-      ivect[irq] = intnull;
-      iunit[irq] = irq;
-    }
-
   restore_flags (flags);
 
   intr_count--;
@@ -219,53 +196,6 @@ setup_x86_irq (int irq, struct linux_action *new)
   return 0;
 }
 
-int
-install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags,
-                         user_intr_t *user_intr)
-{
-  struct linux_action *action;
-  struct linux_action *old;
-  int retval;
-
-  unsigned int irq = dev->irq[id];
-
-  assert (irq < 16);
-
-  /* Test whether the irq handler has been set */
-  // TODO I need to protect the array when iterating it.
-  old = irq_action[irq];
-  while (old)
-    {
-      if (old->user_intr && old->user_intr->dst_port == user_intr->dst_port)
-       {
-         printk ("The interrupt handler has already been installed on line 
%d", irq);
-         return linux_to_mach_error (-EAGAIN);
-       }
-      old = old->next;
-    }
-
-  /*
-   * Hmm... Should I use `kalloc()' ?
-   * By OKUJI Yoshinori.
-   */
-  action = (struct linux_action *)
-    linux_kmalloc (sizeof (struct linux_action), GFP_KERNEL);
-  if (action == NULL)
-    return linux_to_mach_error (-ENOMEM);
-  
-  action->handler = NULL;
-  action->next = NULL;
-  action->dev_id = NULL;
-  action->flags = SA_SHIRQ;
-  action->user_intr = user_intr;
-  
-  retval = setup_x86_irq (irq, action);
-  if (retval)
-    linux_kfree (action);
-  
-  return linux_to_mach_error (retval);
-}
-
 /*
  * Attach a handler to an IRQ.
  */
@@ -294,7 +224,6 @@ request_irq (unsigned int irq, void (*handler) (int, void 
*, struct pt_regs *),
   action->next = NULL;
   action->dev_id = dev_id;
   action->flags = flags;
-  action->user_intr = NULL;
   
   retval = setup_x86_irq (irq, action);
   if (retval)
-- 
2.28.0.rc1





reply via email to

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