From: Thomas Veerman Date: Fri, 8 Apr 2011 14:53:55 +0000 (+0000) Subject: Reduce the number of copies done by the kernel when handling retrieval and X-Git-Tag: v3.2.0~587 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/FAQ?a=commitdiff_plain;h=9db2311b2256ff39c10d3092e50a489400d4be70;p=minix.git Reduce the number of copies done by the kernel when handling retrieval and delivery of asynchronous messages. --- diff --git a/kernel/proc.c b/kernel/proc.c index 88baa17b9..badb72f70 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -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; ip_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)); + r = 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; is_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); } /*===========================================================================*