Clean and support for asynchronous sends.

This commit is contained in:
Philip Homburg 2007-04-23 14:24:30 +00:00
parent 47233bdf30
commit c59b23859e

View file

@ -42,6 +42,7 @@
#include "debug.h"
#include "kernel.h"
#include "proc.h"
#include <stddef.h>
#include <signal.h>
#include <minix/portio.h>
@ -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",
call_nr, proc_nr(caller_ptr), src_dst_p);
#endif
return(ETRAPDENIED); /* trap denied by mask or kernel */
}
#if 0
if ((iskerneln(src_dst_p) && _function != SENDREC
&& _function != RECEIVE)) {
#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);
#endif
return(ETRAPDENIED); /* trap denied by mask or kernel */
}
#endif
/* 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
/* 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_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 (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: invalid message pointer, trap %d, caller %d\n",
function, proc_nr(caller_ptr));
kprintf(
"sys_call: invalid message pointer, trap %d, caller %d\n",
call_nr, proc_nr(caller_ptr));
#endif
return(EFAULT); /* invalid message pointer */
}
}
/* 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 (function & CHECK_DST) {
if (! get_sys_bit(priv(caller_ptr)->s_ipc_to, nr_to_id(src_dst))) {
#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);
#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; i<size; i++)
{
/* Read status word */
phys_copy(tab_phys + i*sizeof(table[0]) +
offsetof(struct asynmsg, flags),
vir2phys(&tabent.flags), sizeof(tabent.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) ||
!(flags & AMF_VALID))
{
return EINVAL;
}
/* Skip entry is AMF_DONE is already set */
if (flags & AMF_DONE)
continue;
/* Get destination */
phys_copy(tab_phys + i*sizeof(table[0]) +
offsetof(struct asynmsg, dst),
vir2phys(&tabent.dst), sizeof(tabent.dst));
if (!isokendpt(tabent.dst, &dst_p))
{
/* Bad destination, report the error */
tabent.result= EDEADSRCDST;
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;
}
#if 0
kprintf("mini_senda: entry[%d]: flags 0x%x dst %d/%d\n",
i, tabent.flags, tabent.dst, dst_p);
#endif
dst_ptr = proc_addr(dst_p);
/* NO_ENDPOINT should be removed */
if (dst_ptr->p_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; i<size; i++)
{
/* Read status word */
phys_copy(tab_phys + i*sizeof(tabent) +
offsetof(struct asynmsg, flags),
vir2phys(&tabent.flags), sizeof(tabent.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) ||
!(flags & AMF_VALID))
{
kprintf("try_one: bad bits in table\n");
privp->s_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;
}