]> Zhao Yanbai Git Server - minix.git/commitdiff
Reduce the number of copies done by the kernel when handling retrieval and
authorThomas Veerman <thomas@minix3.org>
Fri, 8 Apr 2011 14:53:55 +0000 (14:53 +0000)
committerThomas Veerman <thomas@minix3.org>
Fri, 8 Apr 2011 14:53:55 +0000 (14:53 +0000)
delivery of asynchronous messages.

kernel/proc.c

index 88baa17b9842b29b7571b5e564797a14f7cbdd1e..badb72f706c6865bfa2af0a9af2f021e1dcdc488 100644 (file)
@@ -875,12 +875,11 @@ PRIVATE int mini_receive(struct proc * caller_ptr,
     }
 
     /* Check if there are pending senda(). */
-    if (caller_ptr->p_misc_flags & MF_ASYNMSG)
-    {
+    if (caller_ptr->p_misc_flags & MF_ASYNMSG) {
        if (src_e != ANY)
-               r= try_one(proc_addr(src_p), caller_ptr, NULL);
+               r = try_one(proc_addr(src_p), caller_ptr, NULL);
        else
-               r= try_async(caller_ptr);
+               r = try_async(caller_ptr);
 
        if (r == OK) {
                IPC_STATUS_ADD_CALL(caller_ptr, SENDA);
@@ -1009,7 +1008,7 @@ PUBLIC int mini_notify(
        "(%d/%d, tab 0x%lx)\n",__FILE__,__LINE__,       \
 field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
 
-#define A_RETRIEVE(entry, field)       \
+#define A_RETR_FLD(entry, field)       \
   if(data_copy(caller_ptr->p_endpoint, \
         table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\
                KERNEL, (vir_bytes) &tabent.field,      \
@@ -1018,7 +1017,17 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
                return EFAULT; \
        }
 
-#define A_INSERT(entry, field) \
+#define A_RETR(entry) do {                     \
+  if (data_copy(                               \
+               caller_ptr->p_endpoint, table_v + (entry)*sizeof(asynmsg_t),\
+               KERNEL, (vir_bytes) &tabent,    \
+               sizeof(tabent)) != OK) {        \
+                       ASCOMPLAIN(caller_ptr, entry, "message entry"); \
+                       return(EFAULT);         \
+  }                                            \
+                        } while(0)
+
+#define A_INSRT_FLD(entry, field)      \
   if(data_copy(KERNEL, (vir_bytes) &tabent.field, \
        caller_ptr->p_endpoint, \
        table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\
@@ -1027,179 +1036,122 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
                return EFAULT; \
        }
 
+#define A_INSRT(entry) do {                    \
+  if (data_copy(KERNEL, (vir_bytes) &tabent,   \
+               caller_ptr->p_endpoint, table_v + (entry)*sizeof(asynmsg_t),\
+               sizeof(tabent)) != OK) {        \
+                       ASCOMPLAIN(caller_ptr, entry, "message entry"); \
+                       return(EFAULT);         \
+  }                                            \
+                         } while(0)    
+
 /*===========================================================================*
  *                             mini_senda                                   *
  *===========================================================================*/
 PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
 {
-       int i, dst_p, done, do_notify;
-       unsigned flags;
-       struct proc *dst_ptr;
-       struct priv *privp;
-       asynmsg_t tabent;
-       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;
-       }
+  int r = OK, i, dst_p, done, do_notify;
+  unsigned flags;
+  endpoint_t dst;
+  struct proc *dst_ptr;
+  struct priv *privp;
+  asynmsg_t tabent;
+  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_asynsize= 0;
+  /* Clear table */
+  privp->s_asyntab = -1;       
+  privp->s_asynsize = 0;
 
-       if (size == 0)
-       {
-               /* Nothing to do, just return */
-               return OK;
-       }
+  if (size == 0) return(OK);  /* Nothing to do, just return */
 
-       /* 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;
-       }
+  /* 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;
-       for (i= 0; i<size; i++)
-       {
-
-               /* Read status word */
-               A_RETRIEVE(i, flags);
-               flags= tabent.flags;
-
-               /* Skip empty entries */
-               if (flags == 0)
-                       continue;
-
-               /* Check for reserved bits in the flags field */
-               if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY) ||
-                       !(flags & AMF_VALID))
-               {
-                       return EINVAL;
-               }
-
-               /* Skip entry if AMF_DONE is already set */
-               if (flags & AMF_DONE)
-                       continue;
-
-               /* Get destination */
-               A_RETRIEVE(i, dst);
-
-               if (!isokendpt(tabent.dst, &dst_p))
-               {
-                       /* Bad destination, report the error */
-                       tabent.result= EDEADSRCDST;
-                       A_INSERT(i, result);
-                       tabent.flags= flags | AMF_DONE;
-                       A_INSERT(i, flags);
-
-                       if (flags & AMF_NOTIFY)
-                               do_notify= 1;
-                       continue;
-               }
-
-               if (iskerneln(dst_p))
-               {
-                       /* Asynchronous sends to the kernel are not allowed */
-                       tabent.result= ECALLDENIED;
-                       A_INSERT(i, result);
-                       tabent.flags= flags | AMF_DONE;
-                       A_INSERT(i, flags);
-
-                       if (flags & AMF_NOTIFY)
-                               do_notify= 1;
-                       continue;
-               }
+  /* Scan the table */
+  do_notify = FALSE;   /* XXX: this doesn't do anything? */
+  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. */
+       int pending_recv = FALSE;
+
+       /* Copy message to kernel */
+       A_RETR(i);
+       flags = tabent.flags;
+       dst = tabent.dst;
+
+       if (flags == 0) continue; /* Skip empty entries */
+
+       /* 'flags' field must contain only valid bits */
+       if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY))
+               return(EINVAL);
+       if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */
+       if (flags & AMF_DONE) continue; /* Already done processing */
+
+       if (!isokendpt(tabent.dst, &dst_p)) 
+               r = EDEADSRCDST; /* Bad destination, report the error */
+       else if (iskerneln(dst_p)) 
+               r = ECALLDENIED; /* Asyn sends to the kernel are not allowed */
+       else if (!may_send_to(caller_ptr, dst_p)) 
+               r = ECALLDENIED; /* Send denied by IPC mask */
+       else    /* r == OK */
+               dst_ptr = proc_addr(dst_p);
 
-               if (!may_send_to(caller_ptr, dst_p))
-               {
-                       /* Send denied by IPC mask */
-                       tabent.result= ECALLDENIED;
-                       A_INSERT(i, result);
-                       tabent.flags= flags | AMF_DONE;
-                       A_INSERT(i, flags);
-
-                       if (flags & AMF_NOTIFY)
-                               do_notify= 1;
-                       continue;
-               }
+       /* XXX: RTS_NO_ENDPOINT should be removed */
+       if (r == OK && RTS_ISSET(dst_ptr, RTS_NO_ENDPOINT)) {
+               r = EDEADSRCDST;
+       }
 
-#if 0
-               printf("mini_senda: entry[%d]: flags 0x%x dst %d/%d\n",
-                       i, tabent.flags, tabent.dst, dst_p);
-#endif
+       /* Check if 'dst' is blocked waiting for this message.
+        * If AMF_NOREPLY is set, do not satisfy the receiving part of
+        * a SENDREC.
+        */
+       if (r == OK && WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) &&
+           (!(flags&AMF_NOREPLY) || !(dst_ptr->p_misc_flags&MF_REPLY_PEND))) {
+               /* Destination is indeed waiting for this message. */
+               dst_ptr->p_delivermsg = tabent.msg;
+               dst_ptr->p_delivermsg.m_source = caller_ptr->p_endpoint;
+               dst_ptr->p_misc_flags |= MF_DELIVERMSG;
+               IPC_STATUS_ADD_CALL(dst_ptr, SENDA);
+               RTS_UNSET(dst_ptr, RTS_RECEIVING);
+       } else if (r == OK) {
+               /* Should inform receiver that something is pending */
+               dst_ptr->p_misc_flags |= MF_ASYNMSG;
+               pending_recv = TRUE;
+       } 
+
+       if (!pending_recv) flags |= AMF_DONE; /* Done handling message */
+
+       /* Store results */
+       tabent.result = r;
+       tabent.flags = flags;
+       if (flags & AMF_DONE) {
+               if (r != EDEADSRCDST && (flags & AMF_NOTIFY))
+                       do_notify = TRUE; /* XXX: ? */
+               A_INSRT(i);     /* Copy results to caller */
+       } else
+               done = FALSE;
+  }
 
-               dst_ptr = proc_addr(dst_p);
+  if (do_notify) printf("mini_senda: should notify caller\n"); /* XXX: ? */
 
-               /* RTS_NO_ENDPOINT should be removed */
-               if (RTS_ISSET(dst_ptr, RTS_NO_ENDPOINT))
-               {
-                       tabent.result= EDEADSRCDST;
-                       A_INSERT(i, result);
-                       tabent.flags= flags | AMF_DONE;
-                       A_INSERT(i, flags);
-
-                       if (flags & AMF_NOTIFY)
-                               do_notify= TRUE;
-                       continue;
-               }
+  if (!done) {
+       privp->s_asyntab = (vir_bytes) table;
+       privp->s_asynsize = size;
+  }
 
-               /* Check if 'dst' is blocked waiting for this message.
-                * If AMF_NOREPLY is set, do not satisfy the receiving part of
-                * a SENDREC.
-                */
-               if (WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) &&
-                       (!(flags & AMF_NOREPLY) ||
-                       !(dst_ptr->p_misc_flags & MF_REPLY_PEND)))
-               {
-                       /* Destination is indeed waiting for this message. */
-                       /* Copy message from sender. */
-                       if(copy_msg_from_user(caller_ptr, &table[i].msg,
-                                               &dst_ptr->p_delivermsg))
-                               tabent.result = EFAULT;
-                       else {
-                               dst_ptr->p_delivermsg.m_source = caller_ptr->p_endpoint;
-                               dst_ptr->p_misc_flags |= MF_DELIVERMSG;
-                               IPC_STATUS_ADD_CALL(dst_ptr, SENDA);
-                               RTS_UNSET(dst_ptr, RTS_RECEIVING);
-                               tabent.result = OK;
-                       }
-
-                       A_INSERT(i, result);
-                       tabent.flags= flags | AMF_DONE;
-                       A_INSERT(i, flags);
-
-                       if (flags & AMF_NOTIFY)
-                               do_notify= 1;
-                       continue;
-               }
-               else 
-               {
-                       /* Should inform receiver that something is pending */
-                       dst_ptr->p_misc_flags |= MF_ASYNMSG;
-                       done= FALSE;
-                       continue;
-               } 
-       }
-       if (do_notify)
-               printf("mini_senda: should notify caller\n");
-       if (!done)
-       {
-               privp->s_asyntab= (vir_bytes)table;
-               privp->s_asynsize= size;
-       }
-       return OK;
+  return(OK);
 }
 
 
@@ -1209,30 +1161,29 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
 PRIVATE int try_async(caller_ptr)
 struct proc *caller_ptr;
 {
-       int r;
-       struct priv *privp;
-       struct proc *src_ptr;
-       int postponed = FALSE;
-
-       /* Try all privilege structures */
-       for (privp = BEG_PRIV_ADDR; privp < END_PRIV_ADDR; ++privp) 
-       {
-               if (privp->s_proc_nr == NONE)
-                       continue;
+  int r;
+  struct priv *privp;
+  struct proc *src_ptr;
+  int postponed = FALSE;
+
+  /* Try all privilege structures */
+  for (privp = BEG_PRIV_ADDR; privp < END_PRIV_ADDR; ++privp)  {
+       if (privp->s_proc_nr == NONE)
+               continue;
 
-               src_ptr= proc_addr(privp->s_proc_nr);
+       src_ptr = proc_addr(privp->s_proc_nr);
 
-               assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG));
-               r= try_one(src_ptr, caller_ptr, &postponed);
-               if (r == OK)
-                       return r;
-       }
+       assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG));
+       = try_one(src_ptr, caller_ptr, &postponed);
+       if (r == OK)
+               return(r);
+  }
 
-       /* Nothing found, clear MF_ASYNMSG unless messages were postponed */
-       if (postponed == FALSE)
-               caller_ptr->p_misc_flags &= ~MF_ASYNMSG;
+  /* Nothing found, clear MF_ASYNMSG unless messages were postponed */
+  if (postponed == FALSE)
+       caller_ptr->p_misc_flags &= ~MF_ASYNMSG;
 
-       return ESRCH;
+  return(ESRCH);
 }
 
 
@@ -1241,103 +1192,97 @@ struct proc *caller_ptr;
  *===========================================================================*/
 PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr, int *postponed)
 {
-       int i, done;
-       unsigned flags;
-       size_t size;
-       endpoint_t dst_e;
-       struct priv *privp;
-       asynmsg_t tabent;
-       vir_bytes table_v;
-       struct proc *caller_ptr;
-
-       privp= priv(src_ptr);
-
-       /* Basic validity checks */
-       if (privp->s_id == USER_PRIV_ID) return EAGAIN;
-       if (privp->s_asynsize == 0) return EAGAIN;
-       if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return EAGAIN;
-
-       size= privp->s_asynsize;
-       table_v = privp->s_asyntab;
-       caller_ptr = src_ptr;
-
-       dst_e= dst_ptr->p_endpoint;
-
-       /* Scan the table */
-       done= TRUE;
-       for (i= 0; i<size; i++)
-       {
-               /* Read status word */
-               A_RETRIEVE(i, flags);
-               flags= tabent.flags;
-
-               /* Skip empty entries */
-               if (flags == 0)
-               {
-                       continue;
-               }
+  int r = EAGAIN, i, done, do_notify, pending_recv = FALSE;
+  unsigned flags;
+  size_t size;
+  endpoint_t dst;
+  struct proc *caller_ptr;
+  struct priv *privp;
+  asynmsg_t tabent;
+  vir_bytes table_v;
+
+  privp = priv(src_ptr);
+  if (!(privp->s_flags & SYS_PROC)) return(EPERM);
+  size = privp->s_asynsize;
+  table_v = privp->s_asyntab;
+
+  /* Clear table */
+  privp->s_asyntab = -1;
+  privp->s_asynsize = 0;
+
+  if (size == 0) return(EAGAIN);
+  if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return(EAGAIN);
+
+  caller_ptr = src_ptr;
+
+  /* Scan the table */
+  do_notify = FALSE;   /* XXX: this doesn't do anything? */
+  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.
+        * Some checks done in mini_senda are duplicated here, as the sender
+        * could've altered the contents of the table in the mean time.
+        */
+
+       /* Copy message to kernel */
+       A_RETR(i);
+       flags = tabent.flags;
+       dst = tabent.dst;
+
+       if (flags == 0) continue;       /* Skip empty entries */
+
+       /* 'flags' field must contain only valid bits */
+       if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY))
+               return(EINVAL);
+       if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */
+       if (flags & AMF_DONE) continue; /* Already done processing */
+
+       /* Clear done flag. The sender is done sending when all messages in the
+        * table are marked done or empty. However, we will know that only
+        * the next time we enter this function or when the sender decides to
+        * send additional asynchronous messages and manages to deliver them
+        * all.
+        */
+       done = FALSE;
 
-               /* Check for reserved bits in the flags field */
-               if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY) ||
-                       !(flags & AMF_VALID))
-               {
-                       printf("try_one: bad bits in table\n");
-                       privp->s_asynsize= 0;
-                       return EINVAL;
-               }
+       /* Message must be directed at receiving end */
+       if (dst != dst_ptr->p_endpoint) continue;
 
-               /* Skip entry is AMF_DONE is already set */
-               if (flags & AMF_DONE)
-               {
-                       continue;
-               }
-
-               /* Clear done. We are done when all entries are either empty
-                * or done at the start of the call.
-                */
-               done= FALSE;
+       /* If AMF_NOREPLY is set, then this message is not a reply to a
+        * SENDREC and thus should not satisfy the receiving part of the
+        * SENDREC. This message is to be delivered later.
+        */
+       if ((flags & AMF_NOREPLY) && (dst_ptr->p_misc_flags & MF_REPLY_PEND)) {
+               if (postponed != NULL)
+                       *postponed = TRUE;
 
-               /* Get destination */
-               A_RETRIEVE(i, dst);
+               continue;
+       }
 
-               if (tabent.dst != dst_e)
-               {
-                       continue;
-               }
+       /* Destination is ready to receive the message; deliver it */
+       dst_ptr->p_delivermsg = tabent.msg;
+       dst_ptr->p_delivermsg.m_source = src_ptr->p_endpoint;
+       dst_ptr->p_misc_flags |= MF_DELIVERMSG;
 
-               /* If AMF_NOREPLY is set, do not satisfy the receiving part of
-                * a SENDREC. Do not unset MF_ASYNMSG later because of this,
-                * though: this message is still to be delivered later.
-                */
-               if ((flags & AMF_NOREPLY) &&
-                       (dst_ptr->p_misc_flags & MF_REPLY_PEND))
-               {
-                       if (postponed != NULL)
-                               *postponed = TRUE;
+       /* Store results for sender */
+       tabent.result = OK;
+       tabent.flags = flags | AMF_DONE;
+       if (flags & AMF_NOTIFY) do_notify = TRUE; /* XXX: ? */
+       A_INSRT(i);     /* Copy results to sender */
 
-                       continue;
-               }
+       r = OK;
+       break;
+  }
 
-               /* Deliver message */
-               A_RETRIEVE(i, msg);
-               dst_ptr->p_delivermsg = tabent.msg;
-               dst_ptr->p_delivermsg.m_source = src_ptr->p_endpoint;
-               dst_ptr->p_misc_flags |= MF_DELIVERMSG;
+  if (do_notify) printf("mini_senda: should notify caller\n"); /* XXX: ? */
 
-               tabent.result = OK;
-               A_INSERT(i, result);
-               tabent.flags= flags | AMF_DONE;
-               A_INSERT(i, flags);
+  if (!done) {
+       privp->s_asyntab = table_v;
+       privp->s_asynsize = size;
+  }
 
-               if (flags & AMF_NOTIFY)
-               {
-                       printf("try_one: should notify caller\n");
-               }
-               return OK;
-       }
-       if (done)
-               privp->s_asynsize= 0;
-       return EAGAIN;
+  return(r);
 }
 
 /*===========================================================================*