]> Zhao Yanbai Git Server - minix.git/commitdiff
IPC server: subscribe to process events on demand 63/3263/3
authorDavid van Moolenbroek <david@minix3.org>
Thu, 17 Dec 2015 12:33:40 +0000 (12:33 +0000)
committerLionel Sambuc <lionel.sambuc@gmail.com>
Sat, 16 Jan 2016 13:04:11 +0000 (14:04 +0100)
As mentioned in previous patches, services may not subscribe to
process events from specific processes only, since this results in
race conditions.  However, the IPC server can safely turn on and off
its entire subscription based on whether any System V IPC semaphores
(and, in the future, message queues) are allocated at all.  Since
the System V IPC facilities are not so commonly used, this removes
the extra round trip from PM to the IPC server and back for caught
signals and process exits in the common case.

Change-Id: I937259034872be32f4e26ab99270f4d475ff6134

minix/servers/ipc/inc.h
minix/servers/ipc/main.c
minix/servers/ipc/sem.c

index e84a27d8e0b5571052b87d7eea954777f1835728..2f5ddd7c88beb289925f5c8e1cfcba62f16be674 100644 (file)
@@ -40,6 +40,9 @@
 #define IPCID_TO_IX(id)                ((id) & 0xffff)
 #define IPCID_TO_SEQ(id)       (((id) >> 16) & 0xffff)
 
+/* main.c */
+void update_sem_sub(int);
+
 /* shm.c */
 int do_shmget(message *);
 int do_shmat(message *);
index 8dd3a1e3625a3d8ca02fbaafc074a15925428d0e..a54c1391d24209085f683b7bf8bf6aa39c233a97 100644 (file)
@@ -1,5 +1,10 @@
 #include "inc.h"
 
+#define SEM_EVENTS     0x01    /* semaphore code wants process events */
+static unsigned int event_mask = 0;
+
+static int verbose = 0;
+
 /*
  * The call table for this service.
  */
@@ -14,8 +19,6 @@ static int (* const call_vec[])(message *) = {
        CALL(IPC_SEMOP)         = do_semop,
 };
 
-static int verbose = 0;
-
 /*
  * Initialize the IPC server.
  */
@@ -23,21 +26,13 @@ static int
 sef_cb_init_fresh(int type __unused, sef_init_info_t * info __unused)
 {
 
-       /*
-        * Subscribe to PM process events.  While it might be tempting to
-        * implement a system that subscribes to events only from processes
-        * that are actually blocked (or using the SysV IPC facilities at all),
-        * this would result in race conditions where subscription could happen
-        * "too late" for an ongoing signal delivery, causing the affected
-        * process to deadlock.  By issuing this one blocking subscription call
-        * at startup, we eliminate all possibilities of such race conditions,
-        * at the cost of receiving notifications for literally all processes.
-        */
-       proceventmask(PROC_EVENT_EXIT | PROC_EVENT_SIGNAL);
-
+       /* Nothing to do. */
        return OK;
 }
 
+/*
+ * The service has received a signal.
+ */
 static void
 sef_cb_signal_handler(int signo)
 {
@@ -55,6 +50,9 @@ sef_cb_signal_handler(int signo)
        printf("IPC: exit with unclean state\n");
 }
 
+/*
+ * Perform SEF initialization.
+ */
 static void
 sef_local_startup(void)
 {
@@ -70,6 +68,83 @@ sef_local_startup(void)
        sef_startup();
 }
 
+/*
+ * Update the process event subscription mask if necessary, after one of the
+ * modules has changed its subscription needs.  This code is set up so that
+ * support for SysV IPC message queues can be added easily later.
+ */
+static void
+update_sub(unsigned int new_mask)
+{
+
+       /* If the old and new mask are not both zero or nonzero, update. */
+       if (!event_mask != !new_mask) {
+               /*
+                * Subscribe to PM process events, or unsubscribe.  While it
+                * might be tempting to implement a system that subscribes to
+                * events only from processes that are actually blocked (or
+                * using the SysV IPC facilities at all), this would result in
+                * race conditions where subscription could happen "too late"
+                * for an ongoing signal delivery, causing the affected process
+                * to deadlock.  Subscribing to events from any other call is
+                * safe however, and we exploit that to limit the kernel-level
+                * message passing overhead in the common case (which is that
+                * the IPC servier is not being used at all).  After we have
+                * unsubscribed, we may still get a few leftover events for the
+                * previous subscription, and we must properly reply to those.
+                */
+               if (new_mask)
+                       proceventmask(PROC_EVENT_EXIT | PROC_EVENT_SIGNAL);
+               else
+                       proceventmask(0);
+       }
+
+       event_mask = new_mask;
+}
+
+/*
+ * Update the process event subscription mask for the semaphore code.
+ */
+void
+update_sem_sub(int want_events)
+{
+       unsigned int new_mask;
+
+       new_mask = event_mask & ~SEM_EVENTS;
+       if (want_events)
+               new_mask |= SEM_EVENTS;
+
+       update_sub(new_mask);
+}
+
+/*
+ * PM sent us a process event message.  Handle it, and reply.
+ */
+static void
+got_proc_event(message * m)
+{
+       endpoint_t endpt;
+       int r, has_exited;
+
+       endpt = m->m_pm_lsys_proc_event.endpt;
+       has_exited = (m->m_pm_lsys_proc_event.event == PROC_EVENT_EXIT);
+
+       /*
+        * Currently, only semaphore handling needs to know about processes
+        * being signaled and exiting.
+        */
+       if (event_mask & SEM_EVENTS)
+               sem_process_event(endpt, has_exited);
+
+       /* Echo the request as a reply back to PM. */
+       m->m_type = PROC_EVENT_REPLY;
+       if ((r = asynsend3(m->m_source, m, AMF_NOREPLY)) != OK)
+               printf("IPC: replying to PM process event failed (%d)\n", r);
+}
+
+/*
+ * The System V IPC server.
+ */
 int
 main(int argc, char ** argv)
 {
@@ -95,19 +170,10 @@ main(int argc, char ** argv)
                        continue;
                }
 
+               /* Process event messages from PM are handled separately. */
                if (m.m_source == PM_PROC_NR && m.m_type == PROC_EVENT) {
-                       /*
-                        * Currently, only semaphore handling needs to know
-                        * about processes being signaled and exiting.
-                        */
-                       sem_process_event(m.m_pm_lsys_proc_event.endpt,
-                           m.m_pm_lsys_proc_event.event == PROC_EVENT_EXIT);
-
-                       /* Echo the request as a reply back to PM. */
-                       m.m_type = PROC_EVENT_REPLY;
-                       if ((r = asynsend3(m.m_source, &m, AMF_NOREPLY)) != OK)
-                               printf("IPC: replying to PM process event "
-                                   "failed (%d)\n", r);
+                       got_proc_event(&m);
+
                        continue;
                }
 
index 1ab6c8a8e6f9bcb7ed472c8cad033e9bd4c40af8..6976826024c3ad7ade7de63ab077bc7858b9f1ad 100644 (file)
@@ -139,8 +139,16 @@ do_semget(message * m)
                TAILQ_INIT(&sem->waiters);
 
                assert(i <= sem_list_nr);
-               if (i == sem_list_nr)
+               if (i == sem_list_nr) {
+                       /*
+                        * If no semaphore sets were allocated before,
+                        * subscribe to process events now.
+                        */
+                       if (sem_list_nr == 0)
+                               update_sem_sub(TRUE /*want_events*/);
+
                        sem_list_nr++;
+               }
        }
 
        m->m_lc_ipc_semget.retid = IXSEQ_TO_IPCID(i, sem->semid_ds.sem_perm);
@@ -264,6 +272,12 @@ remove_set(struct sem_struct * sem)
        while (sem_list_nr > 0 &&
            !(sem_list[sem_list_nr - 1].semid_ds.sem_perm.mode & SEM_ALLOC))
                sem_list_nr--;
+
+       /*
+        * If this was our last semaphore set, unsubscribe from process events.
+        */
+       if (sem_list_nr == 0)
+               update_sem_sub(FALSE /*want_events*/);
 }
 
 /*