From: Philip Homburg Date: Mon, 23 Apr 2007 14:24:30 +0000 (+0000) Subject: Clean and support for asynchronous sends. X-Git-Tag: v3.1.4~412 X-Git-Url: http://zhaoyanbai.com/repos/%22../static/icons/index.html?a=commitdiff_plain;h=c59b23859e3c4e43d3db2a0ae67985525966d8ec;p=minix.git Clean and support for asynchronous sends. --- diff --git a/kernel/proc.c b/kernel/proc.c index 31de06de7..f3fcfc48a 100755 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -42,6 +42,7 @@ #include "debug.h" #include "kernel.h" #include "proc.h" +#include #include #include @@ -54,8 +55,12 @@ FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e, FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, int src, message *m_ptr, unsigned flags)); FORWARD _PROTOTYPE( int mini_notify, (struct proc *caller_ptr, int dst)); +FORWARD _PROTOTYPE( int mini_senda, (struct proc *caller_ptr, + asynmsg_t *table, size_t size)); FORWARD _PROTOTYPE( int deadlock, (int function, register struct proc *caller, int src_dst)); +FORWARD _PROTOTYPE( int try_async, (struct proc *caller_ptr)); +FORWARD _PROTOTYPE( int try_one, (struct proc *src_ptr, struct proc *dst_ptr)); FORWARD _PROTOTYPE( void enqueue, (struct proc *rp)); FORWARD _PROTOTYPE( void dequeue, (struct proc *rp)); FORWARD _PROTOTYPE( void sched, (struct proc *rp, int *queue, int *front)); @@ -95,12 +100,10 @@ long bit_map; /* notification event set or flags */ * (or both). The caller is always given by 'proc_ptr'. */ register struct proc *caller_ptr = proc_ptr; /* get pointer to caller */ - int function = call_nr & SYSCALL_FUNC; /* get system call function */ - unsigned flags = call_nr & SYSCALL_FLAGS; /* get flags */ int mask_entry; /* bit to check in send mask */ int group_size; /* used for deadlock check */ int result; /* the system call's result */ - int src_dst; + int src_dst_p; /* Process slot number */ vir_clicks vlo, vhi; /* virtual clicks containing message to send */ #if 1 @@ -110,70 +113,133 @@ long bit_map; /* notification event set or flags */ return EINVAL; } #endif - - /* Require a valid source and/ or destination process, unless echoing. */ - if (src_dst_e != ANY && function != ECHO) { - if(!isokendpt(src_dst_e, &src_dst)) { + + /* Check destination. SENDA is special because its argument is a table and + * not a single destination. RECEIVE is the only call that accepts ANY (in + * addition to a real endpoint). The other calls (SEND, SENDNB, SENDREC, + * and NOTIFY) require an endpoint to corresponds to a process. In addition, + * it is necessary to check whether a process is allow to send to a given + * destination. For SENDREC we check s_ipc_sendrec, and for SEND, SENDNB, + * and NOTIFY we check s_ipc_to. + */ + if (call_nr == SENDA) + { + /* No destination argument */ + } + else if (src_dst_e == ANY) + { + if (call_nr != RECEIVE) + { #if DEBUG_ENABLE_IPC_WARNINGS - kprintf("sys_call: trap %d by %d with bad endpoint %d\n", - function, proc_nr(caller_ptr), src_dst_e); + kprintf("sys_call: trap %d by %d with bad endpoint %d\n", + call_nr, proc_nr(caller_ptr), src_dst_e); #endif - return EDEADSRCDST; - } - } else src_dst = src_dst_e; + return EINVAL; + } + src_dst_p = src_dst_e; + } + else + { + /* Require a valid source and/or destination process. */ + if(!isokendpt(src_dst_e, &src_dst_p)) { +if (src_dst_e == 0) panic("sys_call: no PM", NO_NUM); +#if DEBUG_ENABLE_IPC_WARNINGS + kprintf("sys_call: trap %d by %d with bad endpoint %d\n", + call_nr, proc_nr(caller_ptr), src_dst_e); +#endif + return EDEADSRCDST; + } + + /* If the call is to send to a process, i.e., for SEND, SENDNB, + * SENDREC or NOTIFY, verify that the caller is allowed to send to + * the given destination. + */ + if (call_nr == SENDREC) + { + if (! get_sys_bit(priv(caller_ptr)->s_ipc_sendrec, + nr_to_id(src_dst_p))) { +#if DEBUG_ENABLE_IPC_WARNINGS + kprintf( + "sys_call: ipc sendrec mask denied trap %d from %d to %d\n", + call_nr, proc_nr(caller_ptr), src_dst_p); +#endif + return(ECALLDENIED); /* call denied by ipc mask */ + } + } + else if (call_nr == SEND || call_nr == SENDNB || call_nr == NOTIFY) + { + if (! get_sys_bit(priv(caller_ptr)->s_ipc_to, + nr_to_id(src_dst_p))) { +#if DEBUG_ENABLE_IPC_WARNINGS + kprintf( + "sys_call: ipc mask denied trap %d from %d to %d\n", + call_nr, proc_nr(caller_ptr), src_dst_p); +#endif + return(ECALLDENIED); /* call denied by ipc mask */ + } + } + } + + /* Only allow non-negative call_nr values less than 32 */ + if (call_nr < 0 || call_nr >= 32) + { +#if DEBUG_ENABLE_IPC_WARNINGS + kprintf("sys_call: trap %d not allowed, caller %d, src_dst %d\n", + call_nr, proc_nr(caller_ptr), src_dst_p); +#endif + return(ETRAPDENIED); /* trap denied by mask or kernel */ + } /* Check if the process has privileges for the requested call. Calls to the * kernel may only be SENDREC, because tasks always reply and may not block * if the caller doesn't do receive(). */ - if (! (priv(caller_ptr)->s_trap_mask & (1 << function)) || - (iskerneln(src_dst) && function != SENDREC - && function != RECEIVE)) { + if (!(priv(caller_ptr)->s_trap_mask & (1 << call_nr))) { #if DEBUG_ENABLE_IPC_WARNINGS kprintf("sys_call: trap %d not allowed, caller %d, src_dst %d\n", - function, proc_nr(caller_ptr), src_dst); + call_nr, proc_nr(caller_ptr), src_dst_p); #endif return(ETRAPDENIED); /* trap denied by mask or kernel */ } - /* If the call involves a message buffer, i.e., for SEND, RECEIVE, SENDREC, - * or ECHO, check the message pointer. This check allows a message to be - * anywhere in data or stack or gap. It will have to be made more elaborate - * for machines which don't have the gap mapped. - */ - if (function & CHECK_PTR) { - vlo = (vir_bytes) m_ptr >> CLICK_SHIFT; - vhi = ((vir_bytes) m_ptr + MESS_SIZE - 1) >> CLICK_SHIFT; - if (vlo < caller_ptr->p_memmap[D].mem_vir || vlo > vhi || - vhi >= caller_ptr->p_memmap[S].mem_vir + - caller_ptr->p_memmap[S].mem_len) { +#if 0 + if ((iskerneln(src_dst_p) && _function != SENDREC + && _function != RECEIVE)) { #if DEBUG_ENABLE_IPC_WARNINGS - kprintf("sys_call: invalid message pointer, trap %d, caller %d\n", - function, proc_nr(caller_ptr)); + kprintf("sys_call: trap %d not allowed, caller %d, src_dst %d\n", + function, proc_nr(caller_ptr), src_dst); #endif - return(EFAULT); /* invalid message pointer */ - } + return(ETRAPDENIED); /* trap denied by mask or kernel */ } +#endif - /* If the call is to send to a process, i.e., for SEND, SENDREC or NOTIFY, - * verify that the caller is allowed to send to the given destination. + /* If the call involves a message buffer, i.e., for SEND, SENDNB, SENDREC, + * or RECEIVE, check the message pointer. This check allows a message to be + * anywhere in data or stack or gap. It will have to be made more elaborate + * for machines which don't have the gap mapped. */ - if (function & CHECK_DST) { - if (! get_sys_bit(priv(caller_ptr)->s_ipc_to, nr_to_id(src_dst))) { + if (call_nr == SEND || call_nr == SENDNB || call_nr == SENDREC || + call_nr == RECEIVE) { + vlo = (vir_bytes) m_ptr >> CLICK_SHIFT; + vhi = ((vir_bytes) m_ptr + MESS_SIZE - 1) >> CLICK_SHIFT; + if (vlo < caller_ptr->p_memmap[D].mem_vir || vlo > vhi || + vhi >= caller_ptr->p_memmap[S].mem_vir + + caller_ptr->p_memmap[S].mem_len) { #if DEBUG_ENABLE_IPC_WARNINGS - kprintf("sys_call: ipc mask denied trap %d from %d to %d\n", - function, proc_nr(caller_ptr), src_dst); + kprintf( + "sys_call: invalid message pointer, trap %d, caller %d\n", + call_nr, proc_nr(caller_ptr)); #endif - return(ECALLDENIED); /* call denied by ipc mask */ - } + return(EFAULT); /* invalid message pointer */ + } } /* Check for a possible deadlock for blocking SEND(REC) and RECEIVE. */ - if (function & CHECK_DEADLOCK) { - if (group_size = deadlock(function, caller_ptr, src_dst)) { + if (call_nr == SEND || call_nr == SENDREC || call_nr == RECEIVE) { + if (group_size = deadlock(call_nr, caller_ptr, src_dst_p)) { #if DEBUG_ENABLE_IPC_WARNINGS kprintf("sys_call: trap %d from %d to %d deadlocked, group size %d\n", - function, proc_nr(caller_ptr), src_dst, group_size); + call_nr, proc_nr(caller_ptr), src_dst_p, group_size); #endif return(ELOCKED); } @@ -184,33 +250,36 @@ long bit_map; /* notification event set or flags */ * - SENDREC: combines SEND and RECEIVE in a single system call * - SEND: sender blocks until its message has been delivered * - RECEIVE: receiver blocks until an acceptable message has arrived - * - NOTIFY: nonblocking call; deliver notification or mark pending - * - ECHO: nonblocking call; directly echo back the message + * - NOTIFY: asynchronous call; deliver notification or mark pending + * - SENDNB: nonblocking send + * - SENDA: list of asynchronous send requests */ - switch(function) { + switch(call_nr) { case SENDREC: - /* A flag is set so that notifications cannot interrupt SENDREC. */ - caller_ptr->p_misc_flags |= REPLY_PENDING; - /* fall through */ + /* A flag is set so that notifications cannot interrupt SENDREC. */ + caller_ptr->p_misc_flags |= REPLY_PENDING; + /* fall through */ case SEND: - result = mini_send(caller_ptr, src_dst_e, m_ptr, flags); - if (function == SEND || result != OK) { - break; /* done, or SEND failed */ - } /* fall through for SENDREC */ + result = mini_send(caller_ptr, src_dst_e, m_ptr, 0 /*flags*/); + if (call_nr == SEND || result != OK) + break; /* done, or SEND failed */ + /* fall through for SENDREC */ case RECEIVE: - if (function == RECEIVE) - caller_ptr->p_misc_flags &= ~REPLY_PENDING; - result = mini_receive(caller_ptr, src_dst_e, m_ptr, flags); - break; + if (call_nr == RECEIVE) + caller_ptr->p_misc_flags &= ~REPLY_PENDING; + result = mini_receive(caller_ptr, src_dst_e, m_ptr, 0 /*flags*/); + break; case NOTIFY: - result = mini_notify(caller_ptr, src_dst); - break; - case ECHO: - CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, caller_ptr, m_ptr); - result = OK; - break; + result = mini_notify(caller_ptr, src_dst_p); + break; + case SENDNB: + result = mini_send(caller_ptr, src_dst_e, m_ptr, NON_BLOCKING); + break; + case SENDA: + result = mini_senda(caller_ptr, (asynmsg_t *)m_ptr, (size_t)src_dst_e); + break; default: - result = EBADCALL; /* illegal system call */ + result = EBADCALL; /* illegal system call */ } /* Now, return the result of the system call to the caller. */ @@ -269,6 +338,7 @@ int src_dst; /* src or dst process */ return(0); /* not a deadlock */ } + /*===========================================================================* * mini_send * *===========================================================================*/ @@ -337,7 +407,7 @@ unsigned flags; /* system call flags */ int bit_nr; sys_map_t *map; bitchunk_t *chunk; - int i, src_id, src_proc_nr, src_p; + int i, r, src_id, src_proc_nr, src_p; if(src_e == ANY) src_p = ANY; else @@ -400,6 +470,24 @@ unsigned flags; /* system call flags */ } xpp = &(*xpp)->p_q_link; /* proceed to next */ } + + if (caller_ptr->p_misc_flags & MF_ASYNMSG) + { + if (src_e != ANY) + { +#if 0 + kprintf("mini_receive: should try async from %d\n", src_e); +#endif + r= EAGAIN; + } + else + { + caller_ptr->p_messbuf = m_ptr; + r= try_async(caller_ptr); + } + if (r == OK) + return OK; /* Got a message */ + } } /* No suitable message is available or the caller couldn't send in SENDREC. @@ -454,6 +542,335 @@ int dst; /* which process to notify */ return(OK); } + +/*===========================================================================* + * mini_senda * + *===========================================================================*/ +PRIVATE int mini_senda(caller_ptr, table, size) +struct proc *caller_ptr; +asynmsg_t *table; +size_t size; +{ + int i, dst_p, done, do_notify; + unsigned flags; + phys_bytes tab_phys; + struct proc *dst_ptr; + struct priv *privp; + message *m_ptr; + asynmsg_t tabent; + + privp= priv(caller_ptr); + if (!(privp->s_flags & SYS_PROC)) + { + kprintf( + "mini_senda: warning caller has no privilege structure\n"); + return EPERM; + } + + /* Clear table */ + privp->s_asyntab= -1; + privp->s_asynsize= 0; + + if (size == 0) + { + /* Nothing to do, just return */ + return OK; + } + + /* Limit size to something reasonable. An arbitrary choice is 16 + * times the number of process table entries. + */ + if (size > 16*(NR_TASKS + NR_PROCS)) + return EDOM; + + /* Map table */ + tab_phys= umap_local(caller_ptr, D, (vir_bytes)table, + size*sizeof(table[0])); + if (tab_phys == 0) + { + kprintf("mini_senda: got bad table pointer/size\n"); + return EFAULT; + } + + /* Scan the table */ + do_notify= FALSE; + done= TRUE; + for (i= 0; ip_rts_flags & NO_ENDPOINT) + { + tabent.result= EDSTDIED; + phys_copy(vir2phys(&tabent.result), + tab_phys + i*sizeof(table[0]) + + offsetof(struct asynmsg, result), + sizeof(tabent.result)); + tabent.flags= flags | AMF_DONE; + phys_copy(vir2phys(&tabent.flags), + tab_phys + i*sizeof(table[0]) + + offsetof(struct asynmsg, flags), + sizeof(tabent.flags)); + + if (flags & AMF_NOTIFY) + do_notify= TRUE; + continue; + } + + /* Check if 'dst' is blocked waiting for this message. The + * destination's SENDING flag may be set when its SENDREC call + * blocked while sending. + */ + if ( (dst_ptr->p_rts_flags & (RECEIVING | SENDING)) == + RECEIVING && + (dst_ptr->p_getfrom_e == ANY || + dst_ptr->p_getfrom_e == caller_ptr->p_endpoint)) + { + /* Destination is indeed waiting for this message. */ + m_ptr= &table[i].msg; /* Note: pointer in the + * caller's address space. + */ + CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr, + dst_ptr->p_messbuf); + + if ((dst_ptr->p_rts_flags &= ~RECEIVING) == 0) + enqueue(dst_ptr); + + tabent.result= OK; + phys_copy(vir2phys(&tabent.result), + tab_phys + i*sizeof(table[0]) + + offsetof(struct asynmsg, result), + sizeof(tabent.result)); + tabent.flags= flags | AMF_DONE; + phys_copy(vir2phys(&tabent.flags), + tab_phys + i*sizeof(table[0]) + + offsetof(struct asynmsg, flags), + sizeof(tabent.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) + kprintf("mini_senda: should notifiy caller\n"); + if (!done) + { + privp->s_asyntab= (vir_bytes)table; + privp->s_asynsize= size; + } + return OK; +} + + +/*===========================================================================* + * try_async * + *===========================================================================*/ +PRIVATE int try_async(caller_ptr) +struct proc *caller_ptr; +{ + int r; + struct priv *privp; + struct proc *src_ptr; + + /* Try all privilege structures */ + for (privp = BEG_PRIV_ADDR; privp < END_PRIV_ADDR; ++privp) + { + if (privp->s_proc_nr == NONE || privp->s_id == USER_PRIV_ID) + continue; + if (privp->s_asynsize == 0) + continue; +#if 0 + kprintf("try_async: found asyntable for proc %d\n", + privp->s_proc_nr); +#endif + src_ptr= proc_addr(privp->s_proc_nr); + r= try_one(src_ptr, caller_ptr); + if (r == OK) + return r; + } + + /* Nothing found, clear MF_ASYNMSG */ + caller_ptr->p_misc_flags &= ~MF_ASYNMSG; + + return ESRCH; +} + + +/*===========================================================================* + * try_one * + *===========================================================================*/ +PRIVATE int try_one(src_ptr, dst_ptr) +struct proc *src_ptr; +struct proc *dst_ptr; +{ + int i, do_notify, done; + unsigned flags; + size_t size; + endpoint_t dst_e; + phys_bytes tab_phys; + asynmsg_t *table_ptr; + message *m_ptr; + struct priv *privp; + asynmsg_t tabent; + + privp= priv(src_ptr); + size= privp->s_asynsize; + + dst_e= dst_ptr->p_endpoint; + + /* Map table */ + tab_phys= umap_local(src_ptr, D, privp->s_asyntab, + size*sizeof(tabent)); + if (tab_phys == 0) + { + kprintf("try_one: got bad table pointer/size\n"); + privp->s_asynsize= 0; + return EFAULT; + } + + /* Scan the table */ + do_notify= FALSE; + done= TRUE; + for (i= 0; is_asynsize= 0; + return EINVAL; + } + + /* 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; + + /* Get destination */ + phys_copy(tab_phys + i*sizeof(tabent) + + offsetof(struct asynmsg, dst), + vir2phys(&tabent.dst), sizeof(tabent.dst)); + + if (tabent.dst != dst_e) + { + kprintf("try_one: wrong dst %d, looking for %d\n", + tabent.dst, dst_e); + continue; + } + +#if 0 + kprintf("try_one: entry[%d]: flags 0x%x dst %d\n", + i, tabent.flags, tabent.dst); +#endif + + /* Deliver message */ + table_ptr= (asynmsg_t *)privp->s_asyntab; + m_ptr= &table_ptr[i].msg; /* Note: pointer in the + * caller's address space. + */ + CopyMess(src_ptr->p_nr, src_ptr, m_ptr, dst_ptr, + dst_ptr->p_messbuf); + + tabent.result= OK; + phys_copy(vir2phys(&tabent.result), + tab_phys + i*sizeof(tabent) + + offsetof(struct asynmsg, result), + sizeof(tabent.result)); + tabent.flags= flags | AMF_DONE; + phys_copy(vir2phys(&tabent.flags), + tab_phys + i*sizeof(tabent) + + offsetof(struct asynmsg, flags), + sizeof(tabent.flags)); + + if (flags & AMF_NOTIFY) + { + kprintf("try_one: should notify caller\n"); + } + return OK; + } + if (done) + privp->s_asynsize= 0; + return EAGAIN; +} + /*===========================================================================* * lock_notify * *===========================================================================*/ @@ -785,4 +1202,3 @@ int *p, fatalflag; } return ok; } -