]> Zhao Yanbai Git Server - minix.git/commitdiff
PM: generic process event publish/subscribe system 61/3261/3
authorDavid van Moolenbroek <david@minix3.org>
Sat, 12 Dec 2015 17:22:00 +0000 (17:22 +0000)
committerLionel Sambuc <lionel.sambuc@gmail.com>
Sat, 16 Jan 2016 13:04:10 +0000 (14:04 +0100)
Now that there are services other than PM and VFS that implement
userland system calls directly, these services may need to know about
events related to user processes.  In particular, signal delivery may
have to interrupt blocking system calls, and certain cleanup tasks may
have to be performed after a user process exits.

This patch aims to implement a generic, lasting solution for this
problem, by allowing services to subscribe to "signal delivered"
and/or "process exit" events from PM.  PM publishes such events by
sending messages to its subscribed services, which must then reply an
acknowledgment message.

For now, only the two aforementioned events are implemented, and only
the IPC service makes use of the process event facility.

The new process event publish/subscribe system replaces the previous
VM notify-sig/watch-exit/query-exit system, which was unsound: 1) it
allowed subscription to events from individual processes, and suffered
from fundamental race conditions as a result; 2) it relied on "not too
many" processes making use of the IPC server functionality in order to
avoid loss of notifications.  In addition, it had the "ipc" process
name hardcoded, did not distinguish between signal delivery and exits,
and added a roundtrip to VM for all events from all processes.

Change-Id: I75ebad4bc54e646c6433f473294cb4003b2c3430

31 files changed:
etc/system.conf
minix/commands/service/parse.c
minix/include/minix/callnr.h
minix/include/minix/com.h
minix/include/minix/ipc.h
minix/include/minix/syslib.h
minix/include/minix/vm.h
minix/lib/libsys/Makefile
minix/lib/libsys/proceventmask.c [new file with mode: 0644]
minix/lib/libsys/vm_notify_sig.c [deleted file]
minix/lib/libsys/vm_query_exit.c [deleted file]
minix/servers/ipc/inc.h
minix/servers/ipc/ipc.conf
minix/servers/ipc/main.c
minix/servers/ipc/sem.c
minix/servers/pm/Makefile
minix/servers/pm/const.h
minix/servers/pm/event.c [new file with mode: 0644]
minix/servers/pm/forkexit.c
minix/servers/pm/main.c
minix/servers/pm/mproc.h
minix/servers/pm/proto.h
minix/servers/pm/signal.c
minix/servers/pm/table.c
minix/servers/pm/trace.c
minix/servers/pm/utility.c
minix/servers/vm/Makefile
minix/servers/vm/main.c
minix/servers/vm/proto.h
minix/servers/vm/queryexit.c [deleted file]
minix/servers/vm/vmproc.h

index 1edd43d0ddbef057a0ec5c0dae68a97d0cf16395..e03d21393878982fcf8a45e6e8037a586d181d40 100644 (file)
@@ -60,7 +60,6 @@ service pm
                FORK            # 01
                EXEC_NEWMEM     # 03
                WILLEXIT        # 05
-               NOTIFY_SIG      # 39
                GETRUSAGE       # 47
                ;
        io      NONE;           # No I/O range allowed
index 593c2ae75613e79bea639b97321a439bb7a816d5..af2d349ef5a9730873ae86f16f6b35917e233683 100644 (file)
@@ -741,9 +741,6 @@ struct
        { "GETPHYS",            VM_GETPHYS },
        { "GETREF",             VM_GETREF },
        { "RS_SET_PRIV",        VM_RS_SET_PRIV },
-       { "QUERY_EXIT",         VM_QUERY_EXIT },
-       { "WATCH_EXIT",         VM_WATCH_EXIT },
-       { "NOTIFY_SIG",         VM_NOTIFY_SIG },
        { "INFO",               VM_INFO },
        { "RS_UPDATE",          VM_RS_UPDATE },
        { "RS_MEMCTL",          VM_RS_MEMCTL },
index 7f5f36d26b9fb17276a8286347e7fc6ea8af83f0..46b91a825f18fee835530098df06c52ee65e291e 100644 (file)
@@ -50,7 +50,7 @@
 #define PM_REBOOT              (PM_BASE + 37)
 #define PM_SVRCTL              (PM_BASE + 38)
 #define PM_SPROF               (PM_BASE + 39)
-/* PM call number 40 is currently unused. */
+#define PM_PROCEVENTMASK       (PM_BASE + 40)
 #define PM_SRV_FORK            (PM_BASE + 41)
 #define PM_SRV_KILL            (PM_BASE + 42)
 #define PM_EXEC_NEW            (PM_BASE + 43)
index 4f611224e64752a03a82adcd28ba72f1a34fd691..9fcb12552b18efc0ed8af260ede3cc0192efc544 100644 (file)
  *===========================================================================*/
 
 #define COMMON_RQ_BASE         0xE00
+#define COMMON_RS_BASE         0xE80
 
 /* Field names for system signals (sent by a signal manager). */
 #define SIGS_SIGNAL_RECEIVED (COMMON_RQ_BASE+0)
 /* Common fault injection ctl request to all processes. */
 #define COMMON_REQ_FI_CTL (COMMON_RQ_BASE+2)
 
+/* Process event message from PM. */
+#define PROC_EVENT             (COMMON_RQ_BASE+3)
+
+/* Reply to process event message to PM. */
+#define PROC_EVENT_REPLY       (COMMON_RS_BASE+0)
+
 /*===========================================================================*
  *                Messages for VM server                                    *
  *===========================================================================*/
 #      define VM_RS_BUF                m2_l1
 #      define VM_RS_SYS                m2_i2
 
-#define VM_QUERY_EXIT          (VM_RQ_BASE+38)
-
-#define VM_NOTIFY_SIG          (VM_RQ_BASE+39)
-#      define VM_NOTIFY_SIG_ENDPOINT   m1_i1
-#      define VM_NOTIFY_SIG_IPC        m1_i2
-
 #define VM_INFO                        (VM_RQ_BASE+40)
 
 /* VM_INFO 'what' values. */
 #      define VM_RS_CTL_ADDR           m2_p1
 #      define VM_RS_CTL_LEN            m2_i3
 
-#define VM_WATCH_EXIT          (VM_RQ_BASE+43)
-
 #define VM_REMAP_RO            (VM_RQ_BASE+44)
 /* same args as VM_REMAP */
 
index b4c18b7e013e2b91a7445f98769bdc62af7533ad..00e25add8b4ac2a2fda80f3118601373a3a43c20 100644 (file)
@@ -1273,6 +1273,13 @@ typedef struct {
 } mess_lsys_pm_getprocnr;
 _ASSERT_MSG_SIZE(mess_lsys_pm_getprocnr);
 
+typedef struct {
+       unsigned int mask;
+
+       uint8_t padding[52];
+} mess_lsys_pm_proceventmask;
+_ASSERT_MSG_SIZE(mess_lsys_pm_proceventmask);
+
 typedef struct {
        uid_t uid;
        gid_t gid;
@@ -1361,13 +1368,6 @@ typedef struct {
 } mess_lsys_vm_map_phys;
 _ASSERT_MSG_SIZE(mess_lsys_vm_map_phys);
 
-typedef struct {
-       endpoint_t      ret_pt;
-       int             is_more;
-       uint8_t         padding[48];
-} mess_lsys_vm_query_exit;
-_ASSERT_MSG_SIZE(mess_lsys_vm_query_exit);
-
 typedef struct {
        endpoint_t      endpt;
        vir_bytes       addr;
@@ -1403,12 +1403,6 @@ typedef struct {
 } mess_lsys_vm_vmremap;
 _ASSERT_MSG_SIZE(mess_lsys_vm_vmremap);
 
-typedef struct {
-       endpoint_t      ep;
-       uint8_t         padding[52];
-} mess_lsys_vm_watch_exit;
-_ASSERT_MSG_SIZE(mess_lsys_vm_watch_exit);
-
 typedef struct {
        size_t          oldlen;
        uint8_t         padding[52];
@@ -1564,6 +1558,14 @@ typedef struct {
 } mess_pm_lsys_getprocnr;
 _ASSERT_MSG_SIZE(mess_pm_lsys_getprocnr);
 
+typedef struct {
+       endpoint_t endpt;
+       unsigned int event;
+
+       uint8_t padding[48];
+} mess_pm_lsys_proc_event;
+_ASSERT_MSG_SIZE(mess_pm_lsys_proc_event);
+
 typedef struct {
        int num;
 
@@ -2186,6 +2188,7 @@ typedef struct noxfer_message {
                mess_lsys_pci_busc_get_bar m_lsys_pci_busc_get_bar;
                mess_lsys_pm_getepinfo  m_lsys_pm_getepinfo;
                mess_lsys_pm_getprocnr  m_lsys_pm_getprocnr;
+               mess_lsys_pm_proceventmask m_lsys_pm_proceventmask;
                mess_lsys_pm_srv_fork   m_lsys_pm_srv_fork;
                mess_lsys_sched_scheduling_start m_lsys_sched_scheduling_start;
                mess_lsys_sched_scheduling_stop m_lsys_sched_scheduling_stop;
@@ -2196,12 +2199,10 @@ typedef struct noxfer_message {
                mess_lsys_vm_getref     m_lsys_vm_getref;
                mess_lsys_vm_info       m_lsys_vm_info;
                mess_lsys_vm_map_phys   m_lsys_vm_map_phys;
-               mess_lsys_vm_query_exit m_lsys_vm_query_exit;
                mess_lsys_vm_rusage     m_lsys_vm_rusage;
                mess_lsys_vm_unmap_phys m_lsys_vm_unmap_phys;
                mess_lsys_vm_update     m_lsys_vm_update;
                mess_lsys_vm_vmremap    m_lsys_vm_vmremap;
-               mess_lsys_vm_watch_exit m_lsys_vm_watch_exit;
                mess_mib_lc_sysctl      m_mib_lc_sysctl;
                mess_mmap               m_mmap;
                mess_net_netdrv_dl_conf m_net_netdrv_dl_conf;
@@ -2222,6 +2223,7 @@ typedef struct noxfer_message {
                mess_pm_lexec_exec_new  m_pm_lexec_exec_new;
                mess_pm_lsys_getepinfo  m_pm_lsys_getepinfo;
                mess_pm_lsys_getprocnr  m_pm_lsys_getprocnr;
+               mess_pm_lsys_proc_event m_pm_lsys_proc_event;
                mess_pm_lsys_sigs_signal m_pm_lsys_sigs_signal;
                mess_pm_sched_scheduling_set_nice m_pm_sched_scheduling_set_nice;
                mess_pty_ptyfs_req      m_pty_ptyfs_req;
index c0de856f57ab7605d3b921ab4f6a1ff797dbdb24..45b92364967d5d8348c173d49dc5cb216d4e270b 100644 (file)
@@ -276,5 +276,13 @@ int copyfd(endpoint_t endpt, int fd, int what);
 #define COPYFD_TO      1       /* copy file descriptor to remote process */
 #define COPYFD_CLOSE   2       /* close file descriptor in remote process */
 
+/*
+ * These are also the event numbers used in PROC_EVENT messages, but in order
+ * to allow for subscriptions to multiple events, they form a bit mask.
+ */
+#define PROC_EVENT_EXIT                0x01    /* process has exited */
+#define PROC_EVENT_SIGNAL      0x02    /* process has caught signal */
+int proceventmask(unsigned int mask);
+
 #endif /* _SYSLIB_H */
 
index c8af1b3f815b01c993887adf78d266e1347b88a4..88420ddbb8c5cb755a8b86486a56324c65e17975 100644 (file)
@@ -16,13 +16,10 @@ int vm_getdma(endpoint_t *procp, phys_bytes *basep, phys_bytes *sizep);
 void *vm_map_phys(endpoint_t who, void *physaddr, size_t len);
 int vm_unmap_phys(endpoint_t who, void *vaddr, size_t len);
 
-int vm_notify_sig(endpoint_t ep, endpoint_t ipc_ep);
 int vm_set_priv(endpoint_t ep, void *buf, int sys_proc);
 int vm_update(endpoint_t src_e, endpoint_t dst_e, int flags);
 int vm_memctl(endpoint_t ep, int req, void** addr, size_t *len);
 int vm_prepare(endpoint_t src_e, endpoint_t dst_e, int flags);
-int vm_query_exit(endpoint_t *endpt);
-int vm_watch_exit(endpoint_t ep);
 int minix_vfs_mmap(endpoint_t who, off_t offset, size_t len,
         dev_t dev, ino_t ino, int fd, u32_t vaddr, u16_t clearend, u16_t
        flags);
index 30ce1a845d660a950f298473b1f741fb8b1e2ac5..d19fbe140a9b041974aceb649a0e9d37a33ed723 100644 (file)
@@ -35,6 +35,7 @@ SRCS+=  \
        mapdriver.c \
        optset.c \
        panic.c \
+       proceventmask.c \
        safecopies.c \
        sched_start.c \
        sched_stop.c \
@@ -99,10 +100,8 @@ SRCS+=  \
        vm_info.c \
        vm_map_phys.c \
        vm_memctl.c \
-       vm_notify_sig.c \
        vm_prepare.c \
        vm_procctl.c \
-       vm_query_exit.c \
        vm_set_priv.c \
        vm_update.c
 
diff --git a/minix/lib/libsys/proceventmask.c b/minix/lib/libsys/proceventmask.c
new file mode 100644 (file)
index 0000000..984a3d7
--- /dev/null
@@ -0,0 +1,20 @@
+#include "syslib.h"
+
+#include <string.h>
+
+/*
+ * Subscribe to a certain set of process events from PM.  Subsequent calls will
+ * replace the set, and the empty set (a zero mask) will unsubscribe the caller
+ * altogether.  Usage restrictions apply; see PM's event.c for details.  Return
+ * OK or a negative error code.
+ */
+int
+proceventmask(unsigned int mask)
+{
+       message m;
+
+       memset(&m, 0, sizeof(m));
+       m.m_lsys_pm_proceventmask.mask = mask;
+
+       return _taskcall(PM_PROC_NR, PM_PROCEVENTMASK, &m);
+}
diff --git a/minix/lib/libsys/vm_notify_sig.c b/minix/lib/libsys/vm_notify_sig.c
deleted file mode 100644 (file)
index cf631fa..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-
-#include "syslib.h"
-
-#include <string.h>
-#include <minix/vm.h>
-
-/*===========================================================================*
- *                                vm_notify_sig                                     *
- *===========================================================================*/
-int vm_notify_sig(endpoint_t ep, endpoint_t ipc_ep)
-{
-    message m;
-    int result;
-
-    memset(&m, 0, sizeof(m));
-    m.VM_NOTIFY_SIG_ENDPOINT = ep;
-    m.VM_NOTIFY_SIG_IPC = ipc_ep;
-
-    result = _taskcall(VM_PROC_NR, VM_NOTIFY_SIG, &m);
-    return(result);
-}
-
diff --git a/minix/lib/libsys/vm_query_exit.c b/minix/lib/libsys/vm_query_exit.c
deleted file mode 100644 (file)
index 6d8ca33..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "syslib.h"
-#include <unistd.h>
-#include <string.h>
-
-/*
- * Return a negative error code if the query itself or the processing of the
- * query resulted in an error.
- * Return 1 if there are more processes waiting to be queried.
- * Return 0 if there are no more processes.
- * Note that for the return value of 0 and 1, the 'endpt' is set accordingly.
- */
-int
-vm_query_exit(int *endpt)
-{
-       message m;
-       int r;
-
-       memset(&m, 0, sizeof(m));
-       r = _taskcall(VM_PROC_NR, VM_QUERY_EXIT, &m);
-       if (r != OK)
-               return r;
-       if (endpt == NULL)
-               return EFAULT;
-
-       *endpt = m.m_lsys_vm_query_exit.ret_pt;
-       return (m.m_lsys_vm_query_exit.is_more ? 1 : 0);
-}
-
-int
-vm_watch_exit(endpoint_t ep)
-{
-       message m;
-
-       memset(&m, 0, sizeof(m));
-       m.m_lsys_vm_watch_exit.ep = ep;
-       return _taskcall(VM_PROC_NR, VM_WATCH_EXIT, &m);
-}
index d9728467f6df3b4718861737fccdb083c344a0d5..dde5043934b1761b4cbb6675a82345496e7fa773 100644 (file)
@@ -52,7 +52,7 @@ int do_semget(message *);
 int do_semctl(message *);
 int do_semop(message *);
 int is_sem_nil(void);
-void sem_process_vm_notify(void);
+void sem_process_event(endpoint_t, int);
 
 /* utility.c */
 int check_perm(struct ipc_perm *, endpoint_t, int);
index e2300e6bbf76db23b8e4a1405b511987ddef4d71..8fb8b4aa378555daa3d10b9ee575833347379238 100644 (file)
@@ -14,7 +14,5 @@ service ipc
                SHM_UNMAP
                GETPHYS
                GETREF
-               QUERY_EXIT
-               WATCH_EXIT
                ;
 };
index 948b72b27b8731ed10dd5db75516ef7025e79619..8dd3a1e3625a3d8ca02fbaafc074a15925428d0e 100644 (file)
@@ -23,6 +23,18 @@ 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);
+
        return OK;
 }
 
@@ -78,19 +90,24 @@ main(int argc, char ** argv)
                        printf("IPC: got %d from %d\n", m.m_type, m.m_source);
 
                if (is_ipc_notify(ipc_status)) {
-                       switch (m.m_source) {
-                       case VM_PROC_NR:
-                               /*
-                                * Currently, only semaphore handling needs
-                                * to know about processes exiting.
-                                */
-                               sem_process_vm_notify();
-                               break;
-                       default:
-                               printf("IPC: ignoring notification from %d\n",
-                                   m.m_source);
-                               break;
-                       }
+                       printf("IPC: ignoring notification from %d\n",
+                           m.m_source);
+                       continue;
+               }
+
+               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);
                        continue;
                }
 
@@ -99,16 +116,6 @@ main(int argc, char ** argv)
 
                if (call_index < __arraycount(call_vec) &&
                    call_vec[call_index] != NULL) {
-                       /*
-                        * If any process does an IPC call, we have to know
-                        * about it exiting.  Tell VM to watch it for us.
-                        * TODO: this is not true; limit to affected processes.
-                        */
-                       if (vm_watch_exit(m.m_source) != OK) {
-                               printf("IPC: watch failed on %d\n",
-                                   m.m_source);
-                       }
-
                        r = call_vec[call_index](&m);
                } else
                        r = ENOSYS;
index 88bea83a717e469eeca09ae3767187019c0a4753..8e4fc82e8aeadf44ae9099c63b9bc556d1caf316 100644 (file)
@@ -637,19 +637,18 @@ is_sem_nil(void)
        return (sem_list_nr == 0);
 }
 
+/*
+ * Check whether processes have terminated and/or are about to have a signal
+ * caught, in which case any pending blocking operation must be cancelled.
+ */
 void
-sem_process_vm_notify(void)
+sem_process_event(endpoint_t endpt, int has_exited __unused)
 {
-       endpoint_t endpt;
-       int r;
 
-       /* For each endpoint, check whether it is waiting. */
-       while ((r = vm_query_exit(&endpt)) >= 0) {
-               remove_process(endpt);
-
-               if (r == 0)
-                       break;
-       }
-       if (r < 0)
-               printf("IPC: query exit error (%d)\n", r);
+       /*
+        * As long as we do not support SEM_UNDO, it does not matter whether
+        * the process has exited or has a signal delivered: in both cases, we
+        * need to cancel any blocking semop(2) call.
+        */
+       remove_process(endpt);
 }
index 75f4c540e8f67653c1d1406719863f7254fd20b2..72a9f43a8beb5341784589b9036f9a9dfb07d5b2 100644 (file)
@@ -4,7 +4,7 @@
 PROG=  pm
 SRCS=  main.c forkexit.c exec.c time.c alarm.c \
        signal.c utility.c table.c trace.c getset.c misc.c \
-       profile.c mcontext.c schedule.c
+       profile.c mcontext.c schedule.c event.c
 
 DPADD+=        ${LIBSYS} ${LIBTIMERS}
 LDADD+=        -lsys -ltimers
index b8c92a5495f0050f443cfd131918e3d5db12e32b..01be61f0de4722af44267cdf3794ec14c28e96f6 100644 (file)
@@ -10,6 +10,8 @@
 
 #define NO_TRACER         0    /* process is not being traced */
 
+#define NO_EVENTSUB    ((char)-1) /* no current process event subscriber */
+
 #define MAX_CLOCK_T    ((unsigned long) 1 << ((sizeof(clock_t) * 8) - 1))
 #define MAX_SECS       ( (clock_t) (MAX_CLOCK_T/system_hz) )
                                /* max.secs for setitimer() ((2^31-1)/HZ) */
diff --git a/minix/servers/pm/event.c b/minix/servers/pm/event.c
new file mode 100644 (file)
index 0000000..b78662a
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * This file implements a generic process event publish/subscribe facility.
+ * The facility for use by non-core system services that implement part of the
+ * userland system call interface.  Currently, it supports two events: a
+ * process catching a signal, and a process being terminated.  A subscribing
+ * service would typically use such events to interrupt a blocking system call
+ * and/or clean up process-bound resources.  As of writing, the only service
+ * that uses this facility is the System V IPC server.
+ *
+ * Each of these events will be published to subscribing services right after
+ * VFS has acknowledged that it has processed the same event.  For each
+ * subscriber, in turn, the process will be blocked (with the EVENT_CALL flag
+ * set) until the subscriber acknowledges the event or PM learns that the
+ * subscriber has died.  Thus, each subscriber adds a serialized messaging
+ * roundtrip for each subscribed event.
+ *
+ * The one and only reason for this synchronous, serialized approach is that it
+ * avoids PM queuing up too many asynchronous messages.  In theory, each
+ * running process may have an event pending, and thus, the serial synchronous
+ * approach requires NR_PROCS asynsend slots.  For a parallel synchronous
+ * approach, this would increase to (NR_PROCS*NR_SUBS).  Worse yet, for an
+ * asynchronous event notification approach, the number of messages that PM can
+ * end up queuing is potentially unbounded, so that is certainly not an option.
+ * At this moment, we expect only one subscriber (the IPC server) which makes
+ * the serial vs parallel point less relevant.
+ *
+ * It is not possible to subscribe to events from certain processes only.  If
+ * a service were to subscribe to process events as part of a system call by
+ * a process (e.g., semop(2) in the case of the IPC server), it may subscribe
+ * "too late" and already have missed a signal event for the process calling
+ * semop(2), for example.  Resolving such race conditions would require major
+ * infrastructure changes.
+ *
+ * A server may however change its event subscription mask at runtime, so as to
+ * limit the number of event messages it receives in a crude fashion.  For the
+ * same race-condition reasons, new subscriptions must always be made when
+ * processing a message that is *not* a system call potentially affected by
+ * events.  In the case of the IPC server, it may subscribe to events from
+ * semget(2) but not semop(2).  For signal events, the delay call system
+ * guarantees the safety of this approach; for exit events, the message type
+ * prioritization does (which is not great; see the TODO item in forkexit.c).
+ *
+ * After changing its mask, a subscribing service may still receive messages
+ * for events it is no longer subscribed to.  It should acknowledge these
+ * messages by sending a reply as usual.
+ */
+
+#include "pm.h"
+#include "mproc.h"
+#include <assert.h>
+
+/*
+ * A realistic upper bound for the number of subscribing services.  The process
+ * event notification system adds a round trip to a service for each subscriber
+ * and uses asynchronous messaging to boot, so clearly it does not scale to
+ * numbers larger than this.
+ */
+#define NR_SUBS                4
+
+static struct {
+       endpoint_t endpt;               /* endpoint of subscriber */
+       unsigned int mask;              /* interests bit mask (PROC_EVENT_) */
+       unsigned int waiting;           /* # procs blocked on reply from it */
+} subs[NR_SUBS];
+
+static unsigned int nsubs = 0;
+static unsigned int nested = 0;
+
+/*
+ * For the current event of the given process, as determined by its flags, send
+ * a process event message to the next subscriber, or resume handling the
+ * event itself if there are no more subscribers to notify.
+ */
+static void
+resume_event(struct mproc * rmp)
+{
+       message m;
+       unsigned int i, event;
+       int r;
+
+       assert(rmp->mp_flags & IN_USE);
+       assert(rmp->mp_flags & EVENT_CALL);
+       assert(rmp->mp_eventsub != NO_EVENTSUB);
+
+       /* Which event should we be concerned about? */
+       if (rmp->mp_flags & EXITING)
+               event = PROC_EVENT_EXIT;
+       else if (rmp->mp_flags & UNPAUSED)
+               event = PROC_EVENT_SIGNAL;
+       else
+               panic("unknown event for flags %x", rmp->mp_flags);
+
+       /*
+        * If there are additional services interested in this event, send a
+        * message to the next one.
+        */
+       for (i = rmp->mp_eventsub; i < nsubs; i++, rmp->mp_eventsub++) {
+               if (subs[i].mask & event) {
+                       memset(&m, 0, sizeof(m));
+                       m.m_type = PROC_EVENT;
+                       m.m_pm_lsys_proc_event.endpt = rmp->mp_endpoint;
+                       m.m_pm_lsys_proc_event.event = event;
+
+                       r = asynsend3(subs[i].endpt, &m, AMF_NOREPLY);
+                       if (r != OK)
+                               panic("asynsend failed: %d", r);
+
+                       assert(subs[i].waiting < NR_PROCS);
+                       subs[i].waiting++;
+
+                       return;
+               }
+       }
+
+       /* No more subscribers to be notified, resume the actual event. */
+       rmp->mp_flags &= ~EVENT_CALL;
+       rmp->mp_eventsub = NO_EVENTSUB;
+
+       if (event == PROC_EVENT_EXIT)
+               exit_restart(rmp);
+       else if (event == PROC_EVENT_SIGNAL)
+               restart_sigs(rmp);
+}
+
+/*
+ * Remove a subscriber from the set, forcefully if we have to.  Ensure that
+ * any processes currently subject to process event notification are updated
+ * accordingly, in a way that no services are skipped for process events.
+ */
+static void
+remove_sub(unsigned int slot)
+{
+       struct mproc *rmp;
+       unsigned int i;
+
+       /* The loop below needs the remaining items to be kept in order. */
+       for (i = slot; i < nsubs - 1; i++)
+               subs[i] = subs[i + 1];
+       nsubs--;
+
+       /* Adjust affected processes' event subscriber indexes to match. */
+       for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
+               if ((rmp->mp_flags & (IN_USE | EVENT_CALL)) !=
+                   (IN_USE | EVENT_CALL))
+                       continue;
+               assert(rmp->mp_eventsub != NO_EVENTSUB);
+
+               /*
+                * While resuming a process could trigger new events, event
+                * calls always take place after the corresponding VFS calls,
+                * making this nesting-safe.  Check anyway, because if nesting
+                * does occur, we are in serious (un-debuggable) trouble.
+                */
+               if ((unsigned int)rmp->mp_eventsub == slot) {
+                       nested++;
+                       resume_event(rmp);
+                       nested--;
+               } else if ((unsigned int)rmp->mp_eventsub > slot)
+                       rmp->mp_eventsub--;
+       }
+}
+
+/*
+ * Subscribe to process events.  The given event mask denotes the events in
+ * which the caller is interested.  Multiple calls will each replace the mask,
+ * and a mask of zero will unsubscribe the service from events altogether.
+ * Return OK on success, EPERM if the caller may not register for events, or
+ * ENOMEM if all subscriber slots are in use already.
+ */
+int
+do_proceventmask(void)
+{
+       unsigned int i, mask;
+
+       /* This call is for system services only. */
+       if (!(mp->mp_flags & PRIV_PROC))
+               return EPERM;
+
+       mask = m_in.m_lsys_pm_proceventmask.mask;
+
+       /*
+        * First check if we need to update or remove an existing entry.
+        * We cannot actually remove services for which we are still waiting
+        * for a reply, so set their mask to zero for later removal instead.
+        */
+       for (i = 0; i < nsubs; i++) {
+               if (subs[i].endpt == who_e) {
+                       if (mask == 0 && subs[i].waiting == 0)
+                               remove_sub(i);
+                       else
+                               subs[i].mask = mask;
+                       return OK;
+               }
+       }
+
+       /* Add a new entry, unless the given mask is empty. */
+       if (mask == 0)
+               return OK;
+
+       /* This case should never trigger. */
+       if (nsubs == __arraycount(subs)) {
+               printf("PM: too many process event subscribers!\n");
+               return ENOMEM;
+       }
+
+       subs[nsubs].endpt = who_e;
+       subs[nsubs].mask = mask;
+       nsubs++;
+
+       return OK;
+}
+
+/*
+ * A subscribing service has replied to a process event message from us, or at
+ * least that is what should have happened.  First make sure of this, and then
+ * resume event handling for the affected process.
+ */
+int
+do_proc_event_reply(void)
+{
+       struct mproc *rmp;
+       endpoint_t endpt;
+       unsigned int i, event;
+       int slot;
+
+       assert(nested == 0);
+
+       /*
+        * Is this an accidental call from a misguided user process?
+        * Politely tell it to go away.
+        */
+       if (!(mp->mp_flags & PRIV_PROC))
+               return ENOSYS;
+
+       /*
+        * Ensure that we got the reply that we want.  Since this code is
+        * relatively new, produce lots of warnings for cases that should never
+        * or rarely occur.  Later we can just ignore all mismatching replies.
+        */
+       endpt = m_in.m_pm_lsys_proc_event.endpt;
+       if (pm_isokendpt(endpt, &slot) != OK) {
+               printf("PM: proc event reply from %d for invalid endpt %d\n",
+                   who_e, endpt);
+               return SUSPEND;
+       }
+       rmp = &mproc[slot];
+       if (!(rmp->mp_flags & EVENT_CALL)) {
+               printf("PM: proc event reply from %d for endpt %d, no event\n",
+                   who_e, endpt);
+               return SUSPEND;
+       }
+       if (rmp->mp_eventsub == NO_EVENTSUB ||
+           (unsigned int)rmp->mp_eventsub >= nsubs) {
+               printf("PM: proc event reply from %d for endpt %d index %d\n",
+                   who_e, endpt, rmp->mp_eventsub);
+               return SUSPEND;
+       }
+       i = rmp->mp_eventsub;
+       if (subs[i].endpt != who_e) {
+               printf("PM: proc event reply for %d from %d instead of %d\n",
+                   endpt, who_e, subs[i].endpt);
+               return SUSPEND;
+       }
+
+       if (rmp->mp_flags & EXITING)
+               event = PROC_EVENT_EXIT;
+       else if (rmp->mp_flags & UNPAUSED)
+               event = PROC_EVENT_SIGNAL;
+       else {
+               printf("PM: proc event reply from %d for %d, bad flags %x\n",
+                   who_e, endpt, rmp->mp_flags);
+               return SUSPEND;
+       }
+       if (m_in.m_pm_lsys_proc_event.event != event) {
+               printf("PM: proc event reply from %d for %d for event %d "
+                   "instead of %d\n", who_e, endpt,
+                   m_in.m_pm_lsys_proc_event.event, event);
+               return SUSPEND;
+       }
+       /*
+        * Do NOT check the event against the subscriber's event mask, since a
+        * service may have unsubscribed from an event while it has yet to
+        * process some leftover notifications for that event.  We could decide
+        * not to wait for the replies to those leftover notifications upon
+        * unsubscription, but that could result in problems upon quick
+        * resubscription, and such cases may in fact happen in practice.
+        */
+
+       assert(subs[i].waiting > 0);
+       subs[i].waiting--;
+
+       /*
+        * If we are now no longer waiting for any replies from an already
+        * unsubscribed (but alive) service, remove it from the set now; this
+        * will also resume events for the current process.  In the normal case
+        * however, let the current process move on to the next subscriber if
+        * there are more, and the actual event otherwise.
+        */
+       if (subs[i].mask == 0 && subs[i].waiting == 0) {
+               remove_sub(i);
+       } else {
+               rmp->mp_eventsub++;
+
+               resume_event(rmp);
+       }
+
+       /* In any case, do not reply to this reply message. */
+       return SUSPEND;
+}
+
+/*
+ * Publish a process event to interested subscribers.  The event is determined
+ * from the process flags.  In addition, if the event is a process exit, also 
+ * check if it is a subscribing service that died.
+ */
+void
+publish_event(struct mproc * rmp)
+{
+       unsigned int i;
+
+       assert(nested == 0);
+       assert((rmp->mp_flags & (IN_USE | EVENT_CALL)) == IN_USE);
+       assert(rmp->mp_eventsub == NO_EVENTSUB);
+
+       /*
+        * If a system service exited, we have to check if it was subscribed to
+        * process events.  If so, we have to remove it from the set and resume
+        * any processes blocked on an event call to that service.
+        */
+       if ((rmp->mp_flags & (PRIV_PROC | EXITING)) == (PRIV_PROC | EXITING)) {
+               for (i = 0; i < nsubs; i++) {
+                       if (subs[i].endpt == rmp->mp_endpoint) {
+                               /*
+                                * If the wait count is nonzero, we may or may
+                                * not get additional replies from this service
+                                * later.  Those will be ignored.
+                                */
+                               remove_sub(i);
+
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Either send an event message to the first subscriber, or if there
+        * are no subscribers, resume processing the event right away.
+        */
+       rmp->mp_flags |= EVENT_CALL;
+       rmp->mp_eventsub = 0;
+
+       resume_event(rmp);
+}
index 30dfb5a3de36b2aafdf1cb7fd00de29176d3ff0c..4752d498419b416a7a0e4134af6c3553d6cef2ea 100644 (file)
@@ -112,6 +112,8 @@ int do_fork()
        rmc->mp_interval[i] = 0;        /* reset timer intervals */
   rmc->mp_started = getticks();                /* remember start time, for ps(1) */
 
+  assert(rmc->mp_eventsub == NO_EVENTSUB);
+
   /* Find a free pid for the child and put it in the table. */
   new_pid = get_free_pid();
   rmc->mp_pid = new_pid;       /* assign pid to child */
@@ -207,6 +209,8 @@ int do_srv_fork()
        rmc->mp_interval[i] = 0;        /* reset timer intervals */
   rmc->mp_started = getticks();                /* remember start time, for ps(1) */
 
+  assert(rmc->mp_eventsub == NO_EVENTSUB);
+
   /* Find a free pid for the child and put it in the table. */
   new_pid = get_free_pid();
   rmc->mp_pid = new_pid;       /* assign pid to child */
@@ -306,6 +310,11 @@ int dump_core;                     /* flag indicating whether to dump core */
   /* If the process is not yet stopped, we force a stop here. This means that
    * the process may still have a delay call pending. For this reason, the main
    * message loop discards requests from exiting processes.
+   *
+   * TODO: make the kernel discard delayed calls upon forced stops for exits,
+   * so that no service needs to deal with this.  Right now it appears that the
+   * only thing preventing problems with other services is the fact that
+   * regular messages are prioritized over asynchronous messages.
    */
   if (!(rmp->mp_flags & PROC_STOPPED)) {
        if ((r = sys_stop(proc_nr_e)) != OK)            /* stop the process */
@@ -316,7 +325,7 @@ int dump_core;                      /* flag indicating whether to dump core */
   if((r=vm_willexit(proc_nr_e)) != OK) {
        panic("exit_proc: vm_willexit failed: %d", r);
   }
-  vm_notify_sig_wrapper(rmp->mp_endpoint);
+
   if (proc_nr_e == INIT_PROC_NR)
   {
        printf("PM: INIT died with exit status %d; showing stacktrace\n", exit_status);
@@ -328,7 +337,9 @@ int dump_core;                      /* flag indicating whether to dump core */
        panic("exit_proc: VFS died: %d", r);
   }
 
-  /* Tell VFS about the exiting process. */
+  /* Tell VFS, and after that any matching process event subscribers, about the
+   * exiting process.
+   */
   memset(&m, 0, sizeof(m));
   m.m_type = dump_core ? VFS_PM_DUMPCORE : VFS_PM_EXIT;
   m.VFS_PM_ENDPT = rmp->mp_endpoint;
@@ -397,9 +408,7 @@ int dump_core;                      /* flag indicating whether to dump core */
 /*===========================================================================*
  *                             exit_restart                                 *
  *===========================================================================*/
-void exit_restart(rmp, dump_core)
-struct mproc *rmp;             /* pointer to the process being terminated */
-int dump_core;                 /* flag indicating whether to dump core */
+void exit_restart(struct mproc *rmp)
 {
 /* VFS replied to our exit or coredump request. Perform the second half of the
  * exit code.
@@ -425,7 +434,7 @@ int dump_core;                      /* flag indicating whether to dump core */
   rmp->mp_scheduler = NONE;
 
   /* For core dumps, now is the right time to try to contact the parent. */
-  if (dump_core)
+  if (!(rmp->mp_flags & (TRACE_ZOMBIE | ZOMBIE | TOLD_PARENT)))
        zombify(rmp);
 
   if (!(rmp->mp_flags & PRIV_PROC))
@@ -522,7 +531,8 @@ int do_wait4()
                        /* This child meets the pid test and has exited. */
                        waited_for = tell_parent(rp, addr);
 
-                       if (waited_for && !(rp->mp_flags & VFS_CALL))
+                       if (waited_for &&
+                           !(rp->mp_flags & (VFS_CALL | EVENT_CALL)))
                                cleanup(rp);
                        return(SUSPEND);
                }
@@ -633,7 +643,7 @@ int try_cleanup;                    /* clean up the child when done? */
        /* The 'try_cleanup' flag merely saves us from having to be really
         * careful with statement ordering in exit_proc() and exit_restart().
         */
-       if (try_cleanup && !(child->mp_flags & VFS_CALL))
+       if (try_cleanup && !(child->mp_flags & (VFS_CALL | EVENT_CALL)))
                cleanup(child);
   }
   else {
index 86dee877ab3c8fb65dcb513f0bbaa8ae0dd9664c..8146015025797555f7170ff855f736d7c7e8c2d6 100644 (file)
@@ -86,6 +86,8 @@ int main()
                handle_vfs_reply();
 
                result = SUSPEND;               /* don't reply */
+       } else if (call_nr == PROC_EVENT_REPLY) {
+               result = do_proc_event_reply();
        } else if (IS_PM_CALL(call_nr)) {
                /* If the system call number is valid, perform the call. */
                call_index = (unsigned int) (call_nr - PM_BASE);
@@ -146,6 +148,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
        init_timer(&rmp->mp_timer);
        rmp->mp_magic = MP_MAGIC;
        rmp->mp_sigact = mpsigact[rmp - mproc];
+       rmp->mp_eventsub = NO_EVENTSUB;
   }
 
   /* Build the set of signals which cause core dumps, and the set of signals
@@ -345,18 +348,18 @@ static void handle_vfs_reply()
 
        break;
 
-  case VFS_PM_EXIT_REPLY:
-       exit_restart(rmp, FALSE /*dump_core*/);
-
-       break;
-
   case VFS_PM_CORE_REPLY:
        if (m_in.VFS_PM_STATUS == OK)
                rmp->mp_sigstatus |= WCOREFLAG;
 
-       exit_restart(rmp, TRUE /*dump_core*/);
+       /* FALLTHROUGH */
+  case VFS_PM_EXIT_REPLY:
+       assert(rmp->mp_flags & EXITING);
 
-       break;
+       /* Publish the exit event. Continue exiting the process after that. */
+       publish_event(rmp);
+
+       return; /* do not take the default action */
 
   case VFS_PM_FORK_REPLY:
        /* Schedule the newly created process ... */
@@ -401,7 +404,10 @@ static void handle_vfs_reply()
        /* Process is now unpaused */
        rmp->mp_flags |= UNPAUSED;
 
-       break;
+       /* Publish the signal event. Continue with signals only after that. */
+       publish_event(rmp);
+
+       return; /* do not take the default action */
 
   default:
        panic("handle_vfs_reply: unknown reply code: %d", call_nr);
index 02069fab91545d7986fd2ed622a6f030e6e65d68..d279aa2b7c3187bfef17cf1f59b02bc44b44bd06 100644 (file)
@@ -24,6 +24,7 @@ EXTERN ixfer_sigaction mpsigact[NR_PROCS][_NSIG];
 EXTERN struct mproc {
   char mp_exitstatus;          /* storage for status when process exits */
   char mp_sigstatus;           /* storage for signal # for killed procs */
+  char mp_eventsub;            /* process event subscriber, or NO_EVENTSUB */
   pid_t mp_pid;                        /* process id */
   endpoint_t mp_endpoint;      /* kernel endpoint id */
   pid_t mp_procgrp;            /* pid of process group (used for signals) */
@@ -98,5 +99,6 @@ EXTERN struct mproc {
 #define TRACE_ZOMBIE   0x10000 /* waiting for tracer to issue WAIT4 call */
 #define DELAY_CALL     0x20000 /* waiting for call before sending signal */
 #define TAINTED                0x40000 /* process is 'tainted' */
+#define EVENT_CALL     0x80000 /* waiting for process event subscriber */
 
 #define MP_MAGIC       0xC0FFEE0
index 175ddbcdb84d82cc1ef58dffaf5f6bf469b1a54f..3a855438721b178a4c37ffb13b72bbb505abf56f 100644 (file)
@@ -9,6 +9,11 @@ int do_itimer(void);
 void set_alarm(struct mproc *rmp, clock_t ticks);
 void check_vtimer(int proc_nr, int sig);
 
+/* event.c */
+int do_proceventmask(void);
+int do_proc_event_reply(void);
+void publish_event(struct mproc *rmp);
+
 /* exec.c */
 int do_exec(void);
 int do_newexec(void);
@@ -21,7 +26,7 @@ int do_fork(void);
 int do_srv_fork(void);
 int do_exit(void);
 void exit_proc(struct mproc *rmp, int exit_status, int dump_core);
-void exit_restart(struct mproc *rmp, int dump_core);
+void exit_restart(struct mproc *rmp);
 int do_wait4(void);
 int wait_test(struct mproc *rmp, struct mproc *child);
 
@@ -68,7 +73,6 @@ int do_sigreturn(void);
 int do_sigsuspend(void);
 void check_pending(struct mproc *rmp);
 void restart_sigs(struct mproc *rmp);
-void vm_notify_sig_wrapper(endpoint_t ep);
 
 /* time.c */
 int do_stime(void);
index d793d8a15f7049b9e2cf3df95a2987c0f2767299..b766d4e7695d559f48476e313c1a8fa7ea41d033 100644 (file)
@@ -43,7 +43,7 @@ int do_sigaction(void)
   struct sigaction svec;
   struct sigaction *svp;
 
-  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED)));
+  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED | EVENT_CALL)));
 
   sig_nr = m_in.m_lc_pm_sig.nr;
   if (sig_nr == SIGKILL) return(OK);
@@ -90,7 +90,7 @@ int do_sigaction(void)
  *===========================================================================*/
 int do_sigpending(void)
 {
-  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED)));
+  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED | EVENT_CALL)));
 
   mp->mp_reply.m_pm_lc_sigset.set = mp->mp_sigpending;
   return OK;
@@ -114,7 +114,7 @@ int do_sigprocmask(void)
   sigset_t set;
   int i;
 
-  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED)));
+  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED | EVENT_CALL)));
 
   set = m_in.m_lc_pm_sigset.set;
   mp->mp_reply.m_pm_lc_sigset.set = mp->mp_sigmask;
@@ -159,7 +159,7 @@ int do_sigprocmask(void)
  *===========================================================================*/
 int do_sigsuspend(void)
 {
-  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED)));
+  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED | EVENT_CALL)));
 
   mp->mp_sigmask2 = mp->mp_sigmask;    /* save the old mask */
   mp->mp_sigmask = m_in.m_lc_pm_sigset.set;
@@ -180,7 +180,7 @@ int do_sigreturn(void)
  */
   int r;
 
-  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED)));
+  assert(!(mp->mp_flags & (PROC_STOPPED | VFS_CALL | UNPAUSED | EVENT_CALL)));
 
   mp->mp_sigmask = m_in.m_lc_pm_sigset.set;
   sigdelset(&mp->mp_sigmask, SIGKILL);
@@ -270,11 +270,13 @@ static void try_resume_proc(struct mproc *rmp)
 
   assert(rmp->mp_flags & PROC_STOPPED);
 
-  /* If the process is blocked on a VFS call, do not resume it now. Most likely    * it will be unpausing, in which case the process must remain stopped.
-   * Otherwise, it will still be resumed once the VFS call returns. If the
-   * process has died, do not resume it either.
+  /* If the process is blocked on a VFS call or a process event notification,
+   * do not resume it now.  Most likely it will be unpausing, in which case the
+   * process must remain stopped.  Otherwise, it will still be resumed once the
+   * VFS or event call is replied to.  If the process has died, do not resume
+   * it either.
    */
-  if (rmp->mp_flags & (VFS_CALL | EXITING))
+  if (rmp->mp_flags & (VFS_CALL | EVENT_CALL | EXITING))
        return;
 
   if ((r = sys_resume(rmp->mp_endpoint)) != OK)
@@ -354,7 +356,7 @@ int process_ksig(endpoint_t proc_nr_e, int signo)
         * that case, we must wait with further signal processing until VFS has
         * replied. Stop the process.
         */
-       if (rmp->mp_flags & VFS_CALL) {
+       if (rmp->mp_flags & (VFS_CALL | EVENT_CALL)) {
                stop_proc(rmp, FALSE /*may_delay*/);
 
                return OK;
@@ -418,17 +420,17 @@ int ksig;                 /* non-zero means signal comes from kernel  */
        return;
   }
 
-  if (rmp->mp_flags & VFS_CALL) {
+  if (rmp->mp_flags & (VFS_CALL | EVENT_CALL)) {
        sigaddset(&rmp->mp_sigpending, signo);
        if(ksig)
                sigaddset(&rmp->mp_ksigpending, signo);
 
-       /* Process the signal once VFS replies. Stop the process in the
-        * meantime, so that it cannot make another call after the VFS reply
-        * comes in but before we look at its signals again. Since we always
-        * stop the process to deliver signals during a VFS call, the
-        * PROC_STOPPED flag doubles as an indicator in restart_sigs() that
-        * signals must be rechecked after a VFS reply comes in.
+       /* Process the signal once VFS and process event subscribers reply.
+        * Stop the process in the meantime, so that it cannot make another
+        * call after the VFS reply comes in but before we look at its signals
+        * again. Since we always stop the process to deliver signals during a
+        * VFS or event call, the PROC_STOPPED flag doubles as an indicator in
+        * restart_sigs() that signals must be rechecked after a reply arrives.
         */
        if (!(rmp->mp_flags & (PROC_STOPPED | DELAY_CALL))) {
                /* If a VFS call is ongoing and the process is not yet stopped,
@@ -663,7 +665,7 @@ register struct mproc *rmp;
                sigdelset(&rmp->mp_ksigpending, i);
                sig_proc(rmp, i, FALSE /*trace*/, ksig);
 
-               if (rmp->mp_flags & VFS_CALL) {
+               if (rmp->mp_flags & (VFS_CALL | EVENT_CALL)) {
                        /* Signals must be rechecked upon return from the new
                         * VFS call, unless the process was killed. In both
                         * cases, the process is stopped.
@@ -684,7 +686,7 @@ struct mproc *rmp;
 /* VFS has replied to a request from us; do signal-related work.
  */
 
-  if (rmp->mp_flags & (VFS_CALL | EXITING)) return;
+  if (rmp->mp_flags & (VFS_CALL | EVENT_CALL | EXITING)) return;
 
   if (rmp->mp_flags & TRACE_EXIT) {
        /* Tracer requested exit with specific exit value */
@@ -720,7 +722,7 @@ struct mproc *rmp;          /* which process */
  */
   message m;
 
-  assert(!(rmp->mp_flags & VFS_CALL));
+  assert(!(rmp->mp_flags & (VFS_CALL | EVENT_CALL)));
 
   /* If the UNPAUSED flag is set, VFS replied to an earlier unpause request. */
   if (rmp->mp_flags & UNPAUSED) {
@@ -744,9 +746,10 @@ struct mproc *rmp;         /* which process */
        return TRUE;
   }
 
-  /* Not paused in PM. Let VFS try to unpause the process. The process needs to
-   * be stopped for this. If it is not already stopped, try to stop it now. If
-   * that does not succeed immediately, postpone signal delivery.
+  /* Not paused in PM. Let VFS, and after that any matching process event
+   * subscribers, try to unpause the process. The process needs to be stopped
+   * for this. If it is not already stopped, try to stop it now. If that does
+   * not succeed immediately, postpone signal delivery.
    */
   if (!(rmp->mp_flags & PROC_STOPPED) && !stop_proc(rmp, TRUE /*may_delay*/))
        return FALSE;
@@ -757,9 +760,6 @@ struct mproc *rmp;          /* which process */
 
   tell_vfs(rmp, &m);
 
-  /* Also tell VM. */
-  vm_notify_sig_wrapper(rmp->mp_endpoint);
-
   return FALSE;
 }
 
@@ -845,28 +845,3 @@ int signo;                 /* signal to send to process (1 to _NSIG-1) */
 
   return(TRUE);
 }
-
-/*===========================================================================*
- *                             vm_notify_sig_wrapper                        *
- *===========================================================================*/
-void vm_notify_sig_wrapper(endpoint_t ep)
-{
-/* get IPC's endpoint,
- * the reason that we directly get the endpoint
- * instead of from DS server is that otherwise
- * it will cause deadlock between PM, VM and DS.
- */
-  struct mproc *rmp;
-  endpoint_t ipc_ep = 0;
-
-  for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
-       if (!(rmp->mp_flags & IN_USE))
-               continue;
-       if (!strcmp(rmp->mp_name, "ipc")) {
-               ipc_ep = rmp->mp_endpoint;
-               vm_notify_sig(ep, ipc_ep);
-
-               return;
-       }
-  }
-}
index d730a276f06df56f8efcb16864a829e7cd697d8c..8d9b85e390b398310b01907d79e62c047ed83487 100644 (file)
@@ -51,6 +51,7 @@ int (* const call_vec[NR_PM_CALLS])(void) = {
        CALL(PM_REBOOT)         = do_reboot,            /* reboot(2) */
        CALL(PM_SVRCTL)         = do_svrctl,            /* svrctl(2) */
        CALL(PM_SPROF)          = do_sprofile,          /* sprofile(2) */
+       CALL(PM_PROCEVENTMASK)  = do_proceventmask,     /* proceventmask(2) */
        CALL(PM_SRV_FORK)       = do_srv_fork,          /* srv_fork(2) */
        CALL(PM_SRV_KILL)       = do_srv_kill,          /* srv_kill(2) */
        CALL(PM_EXEC_NEW)       = do_newexec,
index e2498a8826eb7874b4b6924159ed94d44fe4e7d1..f742a0a0ddc4d987554da61adb43266c30ec6ed9 100644 (file)
@@ -145,8 +145,8 @@ int do_trace()
   case T_EXIT:         /* exit */
        child->mp_flags |= TRACE_EXIT;
 
-       /* Defer the exit if the traced process has an VFS call pending. */
-       if (child->mp_flags & VFS_CALL)
+       /* Defer the exit if the traced process has a call pending. */
+       if (child->mp_flags & (VFS_CALL | EVENT_CALL))
                child->mp_exitstatus = m_in.m_lc_pm_ptrace.data; /* save it */
        else
                exit_proc(child, m_in.m_lc_pm_ptrace.data,
index c8cd9a9662cc58437abe6be9e4938fb1f63baecc..04a4b1c2fb8586049707fa86599593863ce86aa0 100644 (file)
@@ -128,7 +128,7 @@ message *m_ptr;
  */
   int r;
 
-  if (rmp->mp_flags & VFS_CALL)
+  if (rmp->mp_flags & (VFS_CALL | EVENT_CALL))
        panic("tell_vfs: not idle: %d", m_ptr->m_type);
 
   r = asynsend3(VFS_PROC_NR, m_ptr, AMF_NOREPLY);
index bd874a732188c4effdfed61906ce1415e34a0597..4cd45a530cf192f7ec93723c96d3c80613286ec8 100644 (file)
@@ -4,7 +4,7 @@
 PROG=  vm
 SRCS=  main.c alloc.c utility.c exit.c fork.c break.c \
        mmap.c slaballoc.c region.c pagefaults.c pagetable.c \
-       rs.c queryexit.c pb.c regionavl.c \
+       rs.c pb.c regionavl.c \
        mem_anon.c mem_directphys.c mem_anon_contig.c mem_shared.c      \
        mem_cache.c cache.c vfs.c mem_file.c fdref.c acl.c
 
index 9f939da8c5aca56aa0524f6a969d3f7f58e360d9..d6f5d7b555c84eb25ba9672daaedbfe7f547d470 100644 (file)
@@ -545,7 +545,6 @@ void init_vm(void)
        CALLMAP(VM_FORK, do_fork);
        CALLMAP(VM_BRK, do_brk);
        CALLMAP(VM_WILLEXIT, do_willexit);
-       CALLMAP(VM_NOTIFY_SIG, do_notify_sig);
 
        CALLMAP(VM_PROCCTL, do_procctl_notrans);
 
@@ -566,8 +565,6 @@ void init_vm(void)
        CALLMAP(VM_SHM_UNMAP, do_munmap);
        CALLMAP(VM_GETREF, do_get_refcount);
        CALLMAP(VM_INFO, do_info);
-       CALLMAP(VM_QUERY_EXIT, do_query_exit);
-       CALLMAP(VM_WATCH_EXIT, do_watch_exit);
 
        /* Cache blocks. */
        CALLMAP(VM_MAPCACHEPAGE, do_mapcache);
@@ -578,9 +575,6 @@ void init_vm(void)
        /* getrusage */
        CALLMAP(VM_GETRUSAGE, do_getrusage);
 
-       /* Initialize the structures for queryexit */
-       init_query_exit();
-
        /* Mark VM instances. */
        num_vm_instances = 1;
        vmproc[VM_PROC_NR].vm_flags |= VMF_VM_INSTANCE;
index c46eb5d0670b78321e86b077c21050ac34ac6ff0..ba06f05ceb033448d0a2b6fe7e04870e55f4038b 100644 (file)
@@ -197,12 +197,6 @@ int do_rs_prepare(message *m);
 int do_rs_update(message *m);
 int do_rs_memctl(message *m);
 
-/* queryexit.c */
-int do_query_exit(message *m);
-int do_watch_exit(message *m);
-int do_notify_sig(message *m);
-void init_query_exit(void);
-
 /* pb.c */
 struct phys_block *pb_new(phys_bytes phys);
 void pb_free(struct phys_block *);
diff --git a/minix/servers/vm/queryexit.c b/minix/servers/vm/queryexit.c
deleted file mode 100644 (file)
index 8f21da3..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-
-#define _SYSTEM 1
-
-#include <minix/callnr.h>
-#include <minix/com.h>
-#include <minix/config.h>
-#include <minix/const.h>
-#include <minix/ds.h>
-#include <minix/endpoint.h>
-#include <minix/minlib.h>
-#include <minix/type.h>
-#include <minix/ipc.h>
-#include <minix/sysutil.h>
-#include <minix/syslib.h>
-#include <minix/safecopies.h>
-#include <minix/bitmap.h>
-#include <minix/vm.h>
-#include <minix/ds.h>
-
-#include <errno.h>
-#include <string.h>
-#include <env.h>
-#include <stdio.h>
-
-#include "glo.h"
-#include "proto.h"
-#include "util.h"
-
-struct query_exit_struct {
-       int avail;
-       endpoint_t ep;
-};
-static struct query_exit_struct array[NR_PROCS];
-
-/*===========================================================================*
- *                             do_query_exit                                *
- *===========================================================================*/
-int do_query_exit(message *m)
-{
-       int i, nr;
-       endpoint_t ep = NONE;
-
-       for (i = 0; i < NR_PROCS; i++) {
-               if (!array[i].avail) {
-                       array[i].avail = 1;
-                       ep = array[i].ep;
-                       array[i].ep = 0;
-
-                       break;
-               }
-       }
-
-       nr = 0;
-       for (i = 0; i < NR_PROCS; i++) {
-               if (!array[i].avail)
-                       nr++;
-       }
-       m->m_lsys_vm_query_exit.ret_pt = ep;
-       m->m_lsys_vm_query_exit.is_more = (nr > 0);
-
-       return OK;
-}
-
-/*===========================================================================*
- *                             do_notify_sig                                *
- *===========================================================================*/
-int do_notify_sig(message *m)
-{
-       int i, avails = 0;
-       endpoint_t ep = m->VM_NOTIFY_SIG_ENDPOINT;
-       endpoint_t ipc_ep = m->VM_NOTIFY_SIG_IPC;
-       int r;
-       struct vmproc *vmp;
-       int pslot;
-
-       if(vm_isokendpt(ep, &pslot) != OK) return ESRCH;
-       vmp = &vmproc[pslot];
-
-       /* Only record the event if we've been asked to report it. */
-       if(!(vmp->vm_flags & VMF_WATCHEXIT))
-               return OK;
-
-       for (i = 0; i < NR_PROCS; i++) {
-               /* its signal is already here */
-               if (!array[i].avail && array[i].ep == ep)
-                       goto out;
-               if (array[i].avail)
-                       avails++;
-       }
-       if (!avails) {
-               /* no slot for signals, unlikely */
-               printf("VM: no slot for signals!\n");
-               return ENOMEM;
-       }
-
-       for (i = 0; i < NR_PROCS; i++) {
-               if (array[i].avail) {
-                       array[i].avail = 0;
-                       array[i].ep = ep;
-
-                       break;
-               }
-       }
-
-out:
-       /* only care when IPC server starts up,
-        * and bypass the process to be signal is IPC itself.
-        */
-       if (ipc_ep != 0 && ep != ipc_ep) {
-               r = ipc_notify(ipc_ep);
-               if (r != OK)
-                       printf("VM: ipc_notify error!\n");
-       }
-       return OK;
-}
-
-/*===========================================================================*
- *                             do_watch_exit                                *
- *===========================================================================*/
-int do_watch_exit(message *m)
-{
-       endpoint_t e = m->m_lsys_vm_watch_exit.ep;
-       struct vmproc *vmp;
-       int p;
-       if(vm_isokendpt(e, &p) != OK) return ESRCH;
-       vmp = &vmproc[p];
-       vmp->vm_flags |= VMF_WATCHEXIT;
-
-       return OK;
-}
-
-/*===========================================================================*
- *                             init_query_exit                              *
- *===========================================================================*/
-void init_query_exit(void)
-{
-       int i;
-
-       for (i = 0; i < NR_PROCS; i++) {
-               array[i].avail = 1;
-               array[i].ep = 0;
-       }
-}
-
index 0ec2a8219d5429eb35476e7e975f65425681a7b0..1a562d5086304fd5c1f5ea3c71499543e7fafc51 100644 (file)
@@ -34,7 +34,6 @@ struct vmproc {
 /* Bits for vm_flags */
 #define VMF_INUSE      0x001   /* slot contains a process */
 #define VMF_EXITING    0x002   /* PM is cleaning up this process */
-#define VMF_WATCHEXIT  0x008   /* Store in queryexit table */
 #define VMF_VM_INSTANCE 0x010   /* This is a VM process instance */
 
 #endif