]> Zhao Yanbai Git Server - minix.git/commitdiff
SMP - asyn send SMP safe
authorTomas Hruby <thruby@few.vu.nl>
Tue, 25 Oct 2011 18:32:30 +0000 (18:32 +0000)
committerTomas Hruby <tom@minix3.org>
Fri, 13 Jan 2012 11:30:01 +0000 (11:30 +0000)
- we must not deliver messages from/to unstable address spaces.
  In such a case, we must postpone the delivery. To make sute
  that a process which is expecting an asynchronous message does
  not starve, we must remember that we skipped delivery of some
  messages and we must try to deliver again once the source
  address space is stable again.

kernel/proc.c
kernel/proc.h
kernel/proto.h
kernel/system/do_vmctl.c

index 5f7523c7fbf23cc1503a05ba1cc7c3de802c772f..c83dc291aa6fdb6a7f7a7def1a39352a50c5b635 100644 (file)
@@ -710,6 +710,9 @@ PRIVATE int has_pending(sys_map_t *map, int src_p, int asynm)
 
   int src_id;
   sys_id_t id = NULL_PRIV_ID;
+#ifdef CONFIG_SMP
+  struct proc * p;
+#endif
 
   /* Either check a specific bit in the mask map, or find the first bit set in
    * it (if any), depending on whether the receive was called on a specific
@@ -717,16 +720,48 @@ PRIVATE int has_pending(sys_map_t *map, int src_p, int asynm)
    */
   if (src_p != ANY) {
        src_id = nr_to_id(src_p);
-       if (get_sys_bit(*map, src_id)) 
-               id = src_id;            
+       if (get_sys_bit(*map, src_id)) {
+#ifdef CONFIG_SMP
+               p = proc_addr(id_to_nr(src_id));
+               if (asynm && RTS_ISSET(p, RTS_VMINHIBIT))
+                       p->p_misc_flags |= MF_SENDA_VM_MISS;
+               else
+#endif
+                       id = src_id;
+       }
   } else {
        /* Find a source with a pending message */
        for (src_id = 0; src_id < NR_SYS_PROCS; src_id += BITCHUNK_BITS) {
                if (get_sys_bits(*map, src_id) != 0) {
-                       while(!get_sys_bit(*map, src_id)) src_id++;
-                       break;
+#ifdef CONFIG_SMP
+                       while (src_id < NR_SYS_PROCS) {
+                               while (!get_sys_bit(*map, src_id)) {
+                                       if (src_id == NR_SYS_PROCS)
+                                               goto quit_search;
+                                       src_id++;
+                               }
+                               p = proc_addr(id_to_nr(src_id));
+                               /*
+                                * We must not let kernel fiddle with pages of a
+                                * process which are currently being changed by
+                                * VM.  It is dangerous! So do not report such a
+                                * process as having pending async messages.
+                                * Skip it.
+                                */
+                               if (asynm && RTS_ISSET(p, RTS_VMINHIBIT)) {
+                                       p->p_misc_flags |= MF_SENDA_VM_MISS;
+                                       src_id++;
+                               } else
+                                       goto quit_search;
+                       }
+#else
+                       while (!get_sys_bit(*map, src_id)) src_id++;
+                       goto quit_search;
+#endif
                }
        }
+
+quit_search:
        if (src_id < NR_SYS_PROCS)      /* Found one */
                id = src_id;
   }
@@ -1066,7 +1101,8 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
                KERNEL, (vir_bytes) &tabent.field,      \
                        sizeof(tabent.field)) != OK) {\
                ASCOMPLAIN(caller_ptr, entry, #field);  \
-               return EFAULT; \
+               r = EFAULT; \
+               goto asyn_error; \
        }
 
 #define A_RETR(entry) do {                     \
@@ -1075,7 +1111,8 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
                KERNEL, (vir_bytes) &tabent,    \
                sizeof(tabent)) != OK) {        \
                        ASCOMPLAIN(caller_ptr, entry, "message entry"); \
-                       return(EFAULT);         \
+                       r = EFAULT;             \
+                       goto asyn_error; \
   }                                            \
                         } while(0)
 
@@ -1085,7 +1122,8 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
        table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\
                sizeof(tabent.field)) != OK) {\
                ASCOMPLAIN(caller_ptr, entry, #field);  \
-               return EFAULT; \
+               r = EFAULT; \
+               goto asyn_error; \
        }
 
 #define A_INSRT(entry) do {                    \
@@ -1093,14 +1131,17 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
                caller_ptr->p_endpoint, table_v + (entry)*sizeof(asynmsg_t),\
                sizeof(tabent)) != OK) {        \
                        ASCOMPLAIN(caller_ptr, entry, "message entry"); \
-                       return(EFAULT);         \
+                       r = EFAULT;             \
+                       goto asyn_error; \
   }                                            \
                          } while(0)    
 
 /*===========================================================================*
- *                             mini_senda                                   *
+ *                             try_deliver_senda                            *
  *===========================================================================*/
-PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
+PUBLIC int try_deliver_senda(struct proc *caller_ptr,
+                               asynmsg_t *table,
+                               size_t size)
 {
   int r, dst_p, done, do_notify;
   unsigned int i;
@@ -1112,33 +1153,34 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
   const vir_bytes table_v = (vir_bytes) table;
 
   privp = priv(caller_ptr);
-  if (!(privp->s_flags & SYS_PROC)) {
-       printf( "mini_senda: warning caller has no privilege structure\n");
-       return(EPERM);
-  }
 
   /* Clear table */
-  privp->s_asyntab = -1;       
+  privp->s_asyntab = -1;
   privp->s_asynsize = 0;
 
   if (size == 0) return(OK);  /* Nothing to do, just return */
 
+  /* Scan the table */
+  do_notify = FALSE;
+  done = TRUE;
+
   /* Limit size to something reasonable. An arbitrary choice is 16
    * times the number of process table entries.
    *
    * (this check has been duplicated in sys_call but is left here
    * as a sanity check)
    */
-  if (size > 16*(NR_TASKS + NR_PROCS)) return(EDOM);
-       
-  /* Scan the table */
-  do_notify = FALSE;
-  done = TRUE;
+  if (size > 16*(NR_TASKS + NR_PROCS)) {
+    r = EDOM;
+    return r;
+  }
+
   for (i = 0; i < size; i++) {
        /* Process each entry in the table and store the result in the table.
         * If we're done handling a message, copy the result to the sender. */
        int pending_recv = FALSE;
 
+       dst = NONE;
        /* Copy message to kernel */
        A_RETR(i);
        flags = tabent.flags;
@@ -1147,9 +1189,14 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
        if (flags == 0) continue; /* Skip empty entries */
 
        /* 'flags' field must contain only valid bits */
-       if(flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY|AMF_NOTIFY_ERR))
-               return(EINVAL);
-       if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */
+       if(flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY|AMF_NOTIFY_ERR)) {
+               r = EINVAL;
+               goto asyn_error;
+       }
+       if (!(flags & AMF_VALID)) { /* Must contain message */
+               r = EINVAL;
+               goto asyn_error;
+       }
        if (flags & AMF_DONE) continue; /* Already done processing */
 
        r = OK;
@@ -1196,6 +1243,13 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
        else if (r != OK && (flags & AMF_NOTIFY_ERR))
                do_notify = TRUE;
        A_INSRT(i);     /* Copy results to caller */
+       continue;
+
+asyn_error:
+       if (dst != NONE)
+               printf("KERNEL senda error %d to %d\n", r, dst);
+       else
+               printf("KERNEL senda error %d\n", r);
   }
 
   if (do_notify) 
@@ -1207,6 +1261,24 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
   }
 
   return(OK);
+
+  return r;
+}
+
+/*===========================================================================*
+ *                             mini_senda                                   *
+ *===========================================================================*/
+PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
+{
+  struct priv *privp;
+
+  privp = priv(caller_ptr);
+  if (!(privp->s_flags & SYS_PROC)) {
+       printf( "mini_senda: warning caller has no privilege structure\n");
+       return(EPERM);
+  }
+
+  return try_deliver_senda(caller_ptr, table, size);
 }
 
 
@@ -1233,6 +1305,17 @@ struct proc *caller_ptr;
 
        src_ptr = proc_addr(privp->s_proc_nr);
 
+#ifdef CONFIG_SMP
+       /*
+        * Do not copy from a process which does not have a stable address space
+        * due to VM fiddling with it
+        */
+       if (RTS_ISSET(src_ptr, RTS_VMINHIBIT)) {
+               src_ptr->p_misc_flags |= MF_SENDA_VM_MISS;
+               continue;
+       }
+#endif
+
        assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG));
        if ((r = try_one(src_ptr, caller_ptr)) == OK)
                return(r);
@@ -1273,6 +1356,7 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr)
   /* Scan the table */
   do_notify = FALSE;
   done = TRUE;
+
   for (i = 0; i < size; i++) {
        /* Process each entry in the table and store the result in the table.
         * If we're done handling a message, copy the result to the sender.
@@ -1342,6 +1426,7 @@ store_result:
        set_sys_bit(priv(dst_ptr)->s_asyn_pending, privp->s_id);
   }
 
+asyn_error:
   return(r);
 }
 
@@ -1379,6 +1464,8 @@ PUBLIC int cancel_async(struct proc *src_ptr, struct proc *dst_ptr)
   /* Scan the table */
   do_notify = FALSE;
   done = TRUE;
+
+
   for (i = 0; i < size; i++) {
        /* Process each entry in the table and store the result in the table.
         * If we're done handling a message, copy the result to the sender.
@@ -1424,6 +1511,7 @@ PUBLIC int cancel_async(struct proc *src_ptr, struct proc *dst_ptr)
        privp->s_asynsize = size;
   }
 
+asyn_error:
   return(OK);
 }
 
index 6d212ae7229b8acf220722d0d5412984b4b34f39..4f9d075797fb60d6321c162ae6d37f77db4ad6b4 100644 (file)
@@ -25,8 +25,8 @@ struct proc {
   struct segframe p_seg;       /* segment descriptors */
   proc_nr_t p_nr;              /* number of this process (for fast access) */
   struct priv *p_priv;         /* system privileges structure */
-  u32_t p_rts_flags;           /* process is runnable only if zero */
-  u32_t p_misc_flags;          /* flags that do not suspend the process */
+  volatile u32_t p_rts_flags;  /* process is runnable only if zero */
+  volatile u32_t p_misc_flags; /* flags that do not suspend the process */
 
   char p_priority;             /* current process priority */
   u64_t p_cpu_time_left;       /* time left to use the cpu */
@@ -252,6 +252,10 @@ struct proc {
 #define MF_FLUSH_TLB   0x10000 /* if set, TLB must be flushed before letting
                                   this process run again. Currently it only
                                   applies to SMP */
+#define MF_SENDA_VM_MISS 0x20000 /* set if a processes wanted to receive an asyn
+                                   message from this sender but could not
+                                   because of VM modifying the sender's address
+                                   space*/
 
 /* Magic process table addresses. */
 #define BEG_PROC_ADDR (&proc[0])
index 023d366a278ad72c1941218a7bd3cd87af0fa277..e9fc457d148e7263533e95e462ef9b3803abfe2b 100644 (file)
@@ -67,6 +67,9 @@ _PROTOTYPE( int isokendpt_f, (endpoint_t e, int *p, int f)            );
 _PROTOTYPE( void proc_no_time, (struct proc *p));
 _PROTOTYPE( void reset_proc_accounting, (struct proc *p));
 _PROTOTYPE( void flag_account, (struct proc *p, int flag));
+_PROTOTYPE( int try_deliver_senda, (struct proc *caller_ptr,
+                                       asynmsg_t *table,
+                                       size_t size)                    );
 
 /* start.c */
 _PROTOTYPE( void cstart, (u16_t cs, u16_t ds, u16_t mds,
index 65493973f9a75d2e8132d562c959e2153f44a5d7..e5a945f7f32b799f4f1e61cf2bb3d24d5172662e 100644 (file)
@@ -160,6 +160,13 @@ PUBLIC int do_vmctl(struct proc * caller, message * m_ptr)
                 */
                RTS_UNSET(p, RTS_VMINHIBIT);
 #ifdef CONFIG_SMP
+               if (p->p_misc_flags & MF_SENDA_VM_MISS) {
+                       struct priv *privp;
+                       p->p_misc_flags &= ~MF_SENDA_VM_MISS;
+                       privp = priv(p);
+                       try_deliver_senda(p, (asynmsg_t *) privp->s_asyntab,
+                                                       privp->s_asynsize);
+               }
                /*
                 * We don't know whether kernel has the changed mapping
                 * installed to access userspace memory. And if so, on what CPU.