]> Zhao Yanbai Git Server - minix.git/commitdiff
VFS: during initial mount, receive but block work 98/2998/1
authorDavid van Moolenbroek <david@minix3.org>
Sat, 6 Jun 2015 18:00:34 +0000 (18:00 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Sat, 6 Jun 2015 18:45:23 +0000 (18:45 +0000)
For VFS, initialization is a special case for processing work: PFS
and the ramdisk MFS must be fully mounted before VFS can process any
other requests, in particular from init(8). This case was handled by
receiving reply messages only from the FS service being mounted, but
this effectively disallowed PFS from calling setuid(2) at startup.

This patch lets VFS receive all messages during the mounting process,
but defer processing any new requests. As a result, the FS services
have a bit more freedom in what they can do during startup.

Change-Id: I18275f458952a8d790736a9c9559b27bbef97b7b

minix/servers/vfs/glo.h
minix/servers/vfs/main.c
minix/servers/vfs/proto.h
minix/servers/vfs/worker.c

index 513d86070d6fe5d99ea20d8b1692fe41a40e2459..1138d964683196b38896a52676e56b0e5f742895 100644 (file)
@@ -14,7 +14,6 @@ EXTERN struct fproc *fp;      /* pointer to caller's fproc struct */
 EXTERN int susp_count;         /* number of procs suspended on pipe */
 EXTERN int nr_locks;           /* number of locks currently in place */
 EXTERN int reviving;           /* number of pipe processes to be revived */
-EXTERN int pending;
 EXTERN int sending;
 EXTERN int verbose;
 
index 5d51d61f83a221550aba2c5b6a31bf61be15573b..0c1c7928843fa61097c66a39f8c73e5d1cfd7ff5 100644 (file)
@@ -48,7 +48,6 @@ static int unblock(struct fproc *rfp);
 /* SEF functions and variables. */
 static void sef_local_startup(void);
 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
-static endpoint_t receive_from;
 
 /*===========================================================================*
  *                             main                                         *
@@ -298,7 +297,6 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *info)
   message mess;
   struct rprocpub rprocpub[NR_BOOT_PROCS];
 
-  receive_from = NONE;
   self = NULL;
   verbose = 0;
 
@@ -404,14 +402,13 @@ static void do_init_root(void)
   char *mount_type, *mount_label;
   int r;
 
-  /* Mount the pipe file server. */
-  receive_from = PFS_PROC_NR;
+  /* Disallow requests from e.g. init(8) while doing the initial mounting. */
+  worker_allow(FALSE);
 
+  /* Mount the pipe file server. */
   mount_pfs();
 
   /* Mount the root file system. */
-  receive_from = MFS_PROC_NR;
-
   mount_type = "mfs";       /* FIXME: use boot image process name instead */
   mount_label = "fs_imgrd"; /* FIXME: obtain this from RS */
 
@@ -419,7 +416,9 @@ static void do_init_root(void)
        mount_label);
   if (r != OK)
        panic("Failed to initialize root");
-  receive_from = ANY;
+
+  /* All done with mounting, allow requests now. */
+  worker_allow(TRUE);
 }
 
 /*===========================================================================*
@@ -502,10 +501,8 @@ static void get_work()
   }
 
   for(;;) {
-       assert(receive_from != NONE);
-
        /* Normal case.  No one to revive. Get a useful request. */
-       if ((r = sef_receive(receive_from, &m_in)) != OK) {
+       if ((r = sef_receive(ANY, &m_in)) != OK) {
                panic("VFS: sef_receive error: %d", r);
        }
 
index 21c2b03a53972f1fd3384055ef5e3ecace77198c..ba37fb2ecea481e68068a755ccf9a8c13ec51136 100644 (file)
@@ -336,6 +336,7 @@ void select_unsuspend_by_endpt(endpoint_t proc);
 /* worker.c */
 void worker_init(void);
 int worker_available(void);
+void worker_allow(int allow);
 struct worker_thread *worker_get(thread_t worker_tid);
 void worker_signal(struct worker_thread *worker);
 int worker_can_start(struct fproc *rfp);
index fe026a6f265df29663b84479572a2a7e99fdfee7..7f01aef235d5f67d7bb03ac34a18280fa9705824 100644 (file)
@@ -5,7 +5,11 @@ static void worker_get_work(void);
 static void *worker_main(void *arg);
 static void worker_sleep(void);
 static void worker_wake(struct worker_thread *worker);
+
 static mthread_attr_t tattr;
+static unsigned int pending;
+static unsigned int busy;
+static int block_all;
 
 #ifdef MKCOVERAGE
 # define TH_STACKSIZE (40 * 1024)
@@ -31,6 +35,8 @@ void worker_init(void)
   if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0)
        panic("couldn't set default thread detach state");
   pending = 0;
+  busy = 0;
+  block_all = FALSE;
 
   for (i = 0; i < NR_WTHREADS; i++) {
        wp = &workers[i];
@@ -50,6 +56,79 @@ void worker_init(void)
   yield_all();
 }
 
+/*===========================================================================*
+ *                             worker_assign                                *
+ *===========================================================================*/
+static void worker_assign(struct fproc *rfp)
+{
+/* Assign the work for the given process to a free thread. The caller must
+ * ensure that there is in fact at least one free thread.
+ */
+  struct worker_thread *worker;
+  int i;
+
+  /* Find a free worker thread. */
+  for (i = 0; i < NR_WTHREADS; i++) {
+       worker = &workers[i];
+
+       if (worker->w_fp == NULL)
+               break;
+  }
+  assert(worker != NULL);
+
+  /* Assign work to it. */
+  rfp->fp_worker = worker;
+  worker->w_fp = rfp;
+  busy++;
+
+  worker_wake(worker);
+}
+
+/*===========================================================================*
+ *                             worker_may_do_pending                        *
+ *===========================================================================*/
+static int worker_may_do_pending(void)
+{
+/* Return whether there is a free thread that may do pending work. This is true
+ * only if there is pending work at all, and there is a free non-spare thread
+ * (the spare thread is never used for pending work), and VFS is currently
+ * processing new requests at all (this may not be true during initialization).
+ */
+
+  /* Ordered by likelihood to be false. */
+  return (pending > 0 && worker_available() > 1 && !block_all);
+}
+
+/*===========================================================================*
+ *                             worker_allow                                 *
+ *===========================================================================*/
+void worker_allow(int allow)
+{
+/* Allow or disallow workers to process new work. If disallowed, any new work
+ * will be stored as pending, even when there are free worker threads. There is
+ * no facility to stop active workers. To be used only during initialization!
+ */
+  struct fproc *rfp;
+
+  block_all = !allow;
+
+  if (!worker_may_do_pending())
+       return;
+
+  /* Assign any pending work to workers. */
+  for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
+       if (rfp->fp_flags & FP_PENDING) {
+               rfp->fp_flags &= ~FP_PENDING; /* No longer pending */
+               assert(pending > 0);
+               pending--;
+               worker_assign(rfp);
+
+               if (!worker_may_do_pending())
+                       return;
+       }
+  }
+}
+
 /*===========================================================================*
  *                             worker_get_work                              *
  *===========================================================================*/
@@ -60,16 +139,19 @@ static void worker_get_work(void)
  */
   struct fproc *rfp;
 
-  /* Do we have queued work to do? */
-  if (pending > 0) {
+  assert(self->w_fp == NULL);
+
+  /* Is there pending work, and should we do it? */
+  if (worker_may_do_pending()) {
        /* Find pending work */
        for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
                if (rfp->fp_flags & FP_PENDING) {
                        self->w_fp = rfp;
                        rfp->fp_worker = self;
+                       busy++;
                        rfp->fp_flags &= ~FP_PENDING; /* No longer pending */
+                       assert(pending > 0);
                        pending--;
-                       assert(pending >= 0);
                        return;
                }
        }
@@ -85,13 +167,8 @@ static void worker_get_work(void)
  *===========================================================================*/
 int worker_available(void)
 {
-  int busy, i;
-
-  busy = 0;
-  for (i = 0; i < NR_WTHREADS; i++) {
-       if (workers[i].w_fp != NULL)
-               busy++;
-  }
+/* Return the number of threads that are available, including the spare thread.
+ */
 
   return(NR_WTHREADS - busy);
 }
@@ -146,6 +223,8 @@ static void *worker_main(void *arg)
 
        fp->fp_worker = NULL;
        self->w_fp = NULL;
+       assert(busy > 0);
+       busy--;
   }
 
   return(NULL);        /* Unreachable */
@@ -196,29 +275,21 @@ static void worker_try_activate(struct fproc *rfp, int use_spare)
 /* See if we can wake up a thread to do the work scheduled for the given
  * process. If not, mark the process as having pending work for later.
  */
-  int i, available, needed;
-  struct worker_thread *worker;
+  int needed;
 
   /* Use the last available thread only if requested. Otherwise, leave at least
    * one spare thread for deadlock resolution.
    */
   needed = use_spare ? 1 : 2;
 
-  worker = NULL;
-  for (i = available = 0; i < NR_WTHREADS; i++) {
-       if (workers[i].w_fp == NULL) {
-               if (worker == NULL)
-                       worker = &workers[i];
-               if (++available >= needed)
-                       break;
-       }
-  }
-
-  if (available >= needed) {
-       assert(worker != NULL);
-       rfp->fp_worker = worker;
-       worker->w_fp = rfp;
-       worker_wake(worker);
+  /* Also make sure that doing new work is allowed at all right now, which may
+   * not be the case during VFS initialization. We do always allow callback
+   * calls, i.e., calls that may use the spare thread. The reason is that we do
+   * not support callback calls being marked as pending, so the (entirely
+   * theoretical) exception here may (entirely theoretically) avoid deadlocks.
+   */
+  if (needed <= worker_available() && (!block_all || use_spare)) {
+       worker_assign(rfp);
   } else {
        rfp->fp_flags |= FP_PENDING;
        pending++;