Junling Ma
unread,Aug 2, 2020, 2:53:00 AM8/2/20You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to bug-...@gnu.org
Sorry the previous PATCH-2 was not in plain text. Here is a resend.
—
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