summaryrefslogtreecommitdiff
path: root/debian/patches/700008-Fix-reference-counting-use-dead-name-notification.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/700008-Fix-reference-counting-use-dead-name-notification.patch')
-rw-r--r--debian/patches/700008-Fix-reference-counting-use-dead-name-notification.patch371
1 files changed, 371 insertions, 0 deletions
diff --git a/debian/patches/700008-Fix-reference-counting-use-dead-name-notification.patch b/debian/patches/700008-Fix-reference-counting-use-dead-name-notification.patch
new file mode 100644
index 0000000..5192747
--- /dev/null
+++ b/debian/patches/700008-Fix-reference-counting-use-dead-name-notification.patch
@@ -0,0 +1,371 @@
+From e5921e151ef397b25db7fe85a325728503a77e9b Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Fri, 26 Feb 2016 15:24:45 +0100
+Subject: [PATCH gnumach 08/15] Fix reference counting; use dead-name
+ notification
+
+---
+ device/ds_routines.c | 15 ++---
+ device/interrupt.h | 4 +-
+ device/intr.c | 168 +++++++++++++++++++++++++++++++++++++--------------
+ kern/ipc_kobject.c | 8 +++
+ kern/ipc_kobject.h | 5 +-
+ 5 files changed, 141 insertions(+), 59 deletions(-)
+
+diff --git a/device/ds_routines.c b/device/ds_routines.c
+index 7a5dda5..2b9869a 100644
+--- a/device/ds_routines.c
++++ b/device/ds_routines.c
+@@ -87,6 +87,7 @@
+ #include <device/dev_hdr.h>
+ #include <device/conf.h>
+ #include <device/io_req.h>
++#include <device/interrupt.h>
+ #include <device/ds_routines.h>
+ #include <device/net_status.h>
+ #include <device/device_port.h>
+@@ -332,6 +333,9 @@ experimental_device_intr_register (ipc_port_t master_port, int line,
+ if (master_port != master_device_port)
+ return D_INVALID_OPERATION;
+
++ if (receive_port == MACH_PORT_NULL)
++ return D_INVALID_OPERATION;
++
+ /* XXX: move to arch-specific */
+ if (line < 0 || line >= 16)
+ return D_INVALID_OPERATION;
+@@ -339,17 +343,8 @@ experimental_device_intr_register (ipc_port_t master_port, int line,
+ ret = insert_intr_entry (line, receive_port, &entry);
+ if (ret)
+ return ret;
+- // TODO The original port should be replaced
+- // when the same device driver calls it again,
+- // in order to handle the case that the device driver crashes and restarts.
+- ret = install_user_intr_handler (line, flags, entry);
+-
+- /* If the port is installed successfully, increase its reference by 1.
+- * Thus, the port won't be destroyed after its task is terminated. */
+- if (ret == 0)
+- ip_reference (receive_port);
+
+- return ret;
++ return install_user_intr_handler (line, flags, entry);
+ #endif /* MACH_XEN */
+ }
+
+diff --git a/device/interrupt.h b/device/interrupt.h
+index 0de43c2..8adc02d 100644
+--- a/device/interrupt.h
++++ b/device/interrupt.h
+@@ -3,12 +3,14 @@
+
+ struct intr_entry;
+ boolean_t queue_intr (struct intr_entry *e);
+-int insert_intr_entry (int line, ipc_port_t dest, struct intr_entry **entry);
++kern_return_t insert_intr_entry (int line, ipc_port_t dest,
++ struct intr_entry **entry);
+
+ int install_user_intr_handler (unsigned int line,
+ unsigned long flags,
+ struct intr_entry *entry);
+
++boolean_t intr_entry_notify (mach_msg_header_t *msg);
+ void intr_thread (void);
+
+ #endif /* DEVICE_INTERRUPT_H */
+diff --git a/device/intr.c b/device/intr.c
+index 0fa43c5..7a1eccb 100644
+--- a/device/intr.c
++++ b/device/intr.c
+@@ -1,7 +1,9 @@
+ #include <device/intr.h>
+ #include <device/ds_routines.h>
++#include <ipc/ipc_space.h>
+ #include <kern/queue.h>
+ #include <kern/printf.h>
++#include <mach/notify.h>
+
+ #include "interrupt.h"
+
+@@ -15,12 +17,14 @@ static struct kmem_cache intr_entry_cache;
+
+ struct intr_entry
+ {
++ ipc_port_t port; /* We receive notifications on this port. */
+ queue_chain_t chain;
+ ipc_port_t dest;
+ int line;
+ /* The number of interrupts occur since last run of intr_thread. */
+ int interrupts;
+ };
++typedef struct intr_entry *intr_entry_t;
+
+ static queue_head_t intr_queue;
+ /* The total number of unprocessed interrupts. */
+@@ -30,18 +34,6 @@ static int tot_num_intr;
+ boolean_t
+ queue_intr (struct intr_entry *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->dest && e->dest->ip_references == 1)
+- {
+- /* JW: I don't like running this from an interrupt handler. */
+- ipc_port_release (e->dest);
+- e->dest = NULL;
+- printf ("irq handler %d: release an dead delivery port\n", e->line);
+- return FALSE;
+- }
+-
+ cli ();
+ e->interrupts++;
+ tot_num_intr++;
+@@ -54,20 +46,58 @@ queue_intr (struct intr_entry *e)
+ /* insert an interrupt entry in the queue.
+ * This entry exists in the queue until
+ * the corresponding interrupt port is removed.*/
+-int
++kern_return_t
+ insert_intr_entry (int line, ipc_port_t dest, struct intr_entry **entry)
+ {
+- int err = 0;
++ kern_return_t err = 0;
+ struct intr_entry *e, *new;
+ int free = 0;
++ ipc_port_t dnnotify;
++ ipc_port_request_index_t dnindex;
+
+ new = (struct intr_entry *) kmem_cache_alloc (&intr_entry_cache);
+ if (new == NULL)
+ return D_NO_MEMORY;
++
++ /* Allocate port, keeping a reference for it. */
++ new->port = ipc_port_alloc_kernel ();
++ if (new->port == IP_NULL)
++ {
++ kmem_cache_free (&intr_entry_cache, (vm_offset_t) new);
++ return KERN_RESOURCE_SHORTAGE;
++ }
++
++ /* Associate the port with the object. */
++ ipc_kobject_set (new->port, (ipc_kobject_t) new, IKOT_INTR_ENTRY);
++
+ new->line = line;
+ new->dest = dest;
+ new->interrupts = 0;
+
++ /* Register a dead-name notification so that we are notified if the
++ userspace handler dies. */
++ dnnotify = ipc_port_make_sonce (new->port);
++ ip_lock (dest);
++ /* We use a bogus port name. We don't need it to distinguish the
++ notifications because we register just one per object. */
++ retry:
++ err = ipc_port_dnrequest (dest, (mach_port_t) 1, dnnotify, &dnindex);
++ if (err)
++ {
++ err = ipc_port_dngrow (dest);
++ /* dest is unlocked */
++ if (err != KERN_SUCCESS)
++ {
++ ipc_port_release_sonce (dnnotify);
++ kmem_cache_free (&intr_entry_cache, (vm_offset_t) new);
++ return err;
++ }
++ ip_lock (dest);
++ goto retry;
++ }
++ /* dest is locked. dnindex is only valid until we unlock it and we
++ might decide to cancel. */
++
+ /* check whether the intr entry has been in the queue. */
+ cli ();
+ queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+@@ -84,11 +114,74 @@ insert_intr_entry (int line, ipc_port_t dest, struct intr_entry **entry)
+ out:
+ sti ();
+ if (free)
+- kmem_cache_free (&intr_entry_cache, (vm_offset_t) new);
++ {
++ ipc_port_dncancel (new->dest, (mach_port_t) 1, dnindex);
++ ip_unlock (new->dest);
++ ipc_port_release_sonce (dnnotify);
++ ipc_port_dealloc_kernel (new->port);
++ ipc_port_release_send (new->dest);
++ kmem_cache_free (&intr_entry_cache, (vm_offset_t) new);
++ }
++ else
++ ip_unlock (new->dest);
+ *entry = new;
+ return err;
+ }
+
++/* Lookup a intr_entry object by its port. */
++static intr_entry_t
++intr_entry_port_lookup (ipc_port_t port)
++{
++ struct intr_entry *entry;
++
++ if (!IP_VALID(port))
++ return 0;
++
++ ip_lock (port);
++ if (ip_active (port) && (ip_kotype (port) == IKOT_INTR_ENTRY))
++ entry = (struct intr_entry *) port->ip_kobject;
++ else
++ entry = 0;
++ ip_unlock (port);
++ return entry;
++}
++
++/* Process a dead-name notification for a userspace interrupt handler
++ notification port. */
++boolean_t
++intr_entry_notify (mach_msg_header_t *msg)
++{
++ if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
++ {
++ struct intr_entry *entry;
++ int line;
++ mach_dead_name_notification_t *dn;
++
++ dn = (mach_dead_name_notification_t *) msg;
++ entry = intr_entry_port_lookup
++ ((ipc_port_t) dn->not_header.msgh_remote_port);
++ assert (entry);
++
++ cli ();
++ line = entry->line;
++ assert (!queue_empty (&intr_queue));
++ queue_remove (&intr_queue, entry, struct intr_entry *, chain);
++ sti ();
++
++ ipc_port_dealloc_kernel (entry->port);
++ ipc_port_release_send (entry->dest);
++ kmem_cache_free (&intr_entry_cache, (vm_offset_t) entry);
++
++ printf ("irq handler %d: userspace handler died\n", line);
++ return TRUE;
++ }
++
++ printf ("intr_entry_notify: strange notification %d\n",
++ msg->msgh_id);
++ return FALSE;
++}
++
++
+ mach_intr_notification_t mach_intr_notification_template;
+
+ static void
+@@ -156,43 +249,26 @@ intr_thread ()
+ {
+ assert_wait ((event_t) &intr_thread, FALSE);
+ cli ();
+- while (tot_num_intr)
+- {
+- int del = 0;
+
+- queue_iterate (&intr_queue, e, struct intr_entry *, chain)
++ while (tot_num_intr)
++ queue_iterate (&intr_queue, e, struct intr_entry *, chain)
++ if (e->interrupts)
+ {
+- /* if an entry doesn't have dest port,
+- * we should remove it. */
+- if (e->dest == NULL)
+- {
+- del = 1;
+- break;
+- }
+-
+- if (e->interrupts)
+- {
+- line = e->line;
+- dest = e->dest;
+- e->interrupts--;
+- tot_num_intr--;
+-
+- sti ();
+- deliver_intr (line, dest);
+- cli ();
+- }
+- }
++ line = e->line;
++ dest = e->dest;
++ e->interrupts--;
++ tot_num_intr--;
+
+- /* remove the entry without dest port from the queue and free it. */
+- if (del)
+- {
+- assert (!queue_empty (&intr_queue));
+- queue_remove (&intr_queue, e, struct intr_entry *, chain);
+ sti ();
+- kmem_cache_free (&intr_entry_cache, (vm_offset_t) e);
++ deliver_intr (line, dest);
+ cli ();
++
++ /* We cannot assume that e still exists at this point
++ because we released the lock. Hence we restart the
++ iteration. */
++ break;
+ }
+- }
++
+ sti ();
+ thread_block (thread_no_continuation);
+ }
+diff --git a/kern/ipc_kobject.c b/kern/ipc_kobject.c
+index c65458b..97c632a 100644
+--- a/kern/ipc_kobject.c
++++ b/kern/ipc_kobject.c
+@@ -48,6 +48,7 @@
+ #include <vm/vm_object.h>
+ #include <vm/memory_object_proxy.h>
+ #include <device/ds_routines.h>
++#include <device/interrupt.h>
+
+ #include <kern/mach.server.h>
+ #include <ipc/mach_port.server.h>
+@@ -326,6 +327,10 @@ ipc_kobject_destroy(
+ vm_object_pager_wakeup(port);
+ break;
+
++ case IKOT_INTR_ENTRY:
++ /* Do nothing. */
++ break;
++
+ default:
+ #if MACH_ASSERT
+ printf("ipc_kobject_destroy: port 0x%p, kobj 0x%lx, type %d\n",
+@@ -368,6 +373,9 @@ ipc_kobject_notify(request_header, reply_header)
+ case IKOT_PAGER_PROXY:
+ return memory_object_proxy_notify(request_header);
+
++ case IKOT_INTR_ENTRY:
++ return intr_entry_notify(request_header);
++
+ default:
+ return FALSE;
+ }
+diff --git a/kern/ipc_kobject.h b/kern/ipc_kobject.h
+index 606a66a..6aefea7 100644
+--- a/kern/ipc_kobject.h
++++ b/kern/ipc_kobject.h
+@@ -77,9 +77,10 @@ typedef unsigned int ipc_kobject_type_t;
+ #define IKOT_CLOCK 25
+ #define IKOT_CLOCK_CTRL 26
+ #define IKOT_PAGER_PROXY 27
++#define IKOT_INTR_ENTRY 28
+ /* << new entries here */
+-#define IKOT_UNKNOWN 28 /* magic catchall */
+-#define IKOT_MAX_TYPE 29 /* # of IKOT_ types */
++#define IKOT_UNKNOWN 29 /* magic catchall */
++#define IKOT_MAX_TYPE 30 /* # of IKOT_ types */
+ /* Please keep ipc/ipc_object.c:ikot_print_array up to date */
+
+ #define is_ipc_kobject(ikot) (ikot != IKOT_NONE)
+--
+2.1.4
+