Reduce the number of copies done by the kernel when handling retrieval and

delivery of asynchronous messages.
This commit is contained in:
Thomas Veerman 2011-04-08 14:53:55 +00:00
parent 8200e91073
commit 9db2311b22

View File

@ -875,8 +875,7 @@ PRIVATE int mini_receive(struct proc * caller_ptr,
} }
/* Check if there are pending senda(). */ /* 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) 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 else
@ -1009,7 +1008,7 @@ PUBLIC int mini_notify(
"(%d/%d, tab 0x%lx)\n",__FILE__,__LINE__, \ "(%d/%d, tab 0x%lx)\n",__FILE__,__LINE__, \
field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab) 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, \ if(data_copy(caller_ptr->p_endpoint, \
table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\ table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\
KERNEL, (vir_bytes) &tabent.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; \ 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, \ if(data_copy(KERNEL, (vir_bytes) &tabent.field, \
caller_ptr->p_endpoint, \ caller_ptr->p_endpoint, \
table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\ table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\
@ -1027,35 +1036,39 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab)
return EFAULT; \ 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 * * mini_senda *
*===========================================================================*/ *===========================================================================*/
PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
{ {
int i, dst_p, done, do_notify; int r = OK, i, dst_p, done, do_notify;
unsigned flags; unsigned flags;
endpoint_t dst;
struct proc *dst_ptr; struct proc *dst_ptr;
struct priv *privp; struct priv *privp;
asynmsg_t tabent; asynmsg_t tabent;
const vir_bytes table_v = (vir_bytes) table; const vir_bytes table_v = (vir_bytes) table;
privp = priv(caller_ptr); privp = priv(caller_ptr);
if (!(privp->s_flags & SYS_PROC)) if (!(privp->s_flags & SYS_PROC)) {
{ printf( "mini_senda: warning caller has no privilege structure\n");
printf( return(EPERM);
"mini_senda: warning caller has no privilege structure\n");
return EPERM;
} }
/* Clear table */ /* Clear table */
privp->s_asyntab = -1; privp->s_asyntab = -1;
privp->s_asynsize = 0; privp->s_asynsize = 0;
if (size == 0) if (size == 0) return(OK); /* Nothing to do, just return */
{
/* Nothing to do, just return */
return OK;
}
/* Limit size to something reasonable. An arbitrary choice is 16 /* Limit size to something reasonable. An arbitrary choice is 16
* times the number of process table entries. * times the number of process table entries.
@ -1063,143 +1076,82 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
* (this check has been duplicated in sys_call but is left here * (this check has been duplicated in sys_call but is left here
* as a sanity check) * as a sanity check)
*/ */
if (size > 16*(NR_TASKS + NR_PROCS)) if (size > 16*(NR_TASKS + NR_PROCS)) return(EDOM);
{
return EDOM;
}
/* Scan the table */ /* Scan the table */
do_notify= FALSE; do_notify = FALSE; /* XXX: this doesn't do anything? */
done = TRUE; done = TRUE;
for (i= 0; i<size; i++) 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;
/* Read status word */ /* Copy message to kernel */
A_RETRIEVE(i, flags); A_RETR(i);
flags = tabent.flags; flags = tabent.flags;
dst = tabent.dst;
/* Skip empty entries */ if (flags == 0) continue; /* Skip empty entries */
if (flags == 0)
continue;
/* Check for reserved bits in the flags field */ /* 'flags' field must contain only valid bits */
if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY) || if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY))
!(flags & AMF_VALID)) return(EINVAL);
{ if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */
return EINVAL; if (flags & AMF_DONE) continue; /* Already done processing */
}
/* 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)) if (!isokendpt(tabent.dst, &dst_p))
{ r = EDEADSRCDST; /* Bad destination, report the error */
/* Bad destination, report the error */ else if (iskerneln(dst_p))
tabent.result= EDEADSRCDST; r = ECALLDENIED; /* Asyn sends to the kernel are not allowed */
A_INSERT(i, result); else if (!may_send_to(caller_ptr, dst_p))
tabent.flags= flags | AMF_DONE; r = ECALLDENIED; /* Send denied by IPC mask */
A_INSERT(i, flags); else /* r == OK */
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;
}
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;
}
#if 0
printf("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); dst_ptr = proc_addr(dst_p);
/* RTS_NO_ENDPOINT should be removed */ /* XXX: RTS_NO_ENDPOINT should be removed */
if (RTS_ISSET(dst_ptr, RTS_NO_ENDPOINT)) if (r == OK && RTS_ISSET(dst_ptr, RTS_NO_ENDPOINT)) {
{ r = EDEADSRCDST;
tabent.result= EDEADSRCDST;
A_INSERT(i, result);
tabent.flags= flags | AMF_DONE;
A_INSERT(i, flags);
if (flags & AMF_NOTIFY)
do_notify= TRUE;
continue;
} }
/* Check if 'dst' is blocked waiting for this message. /* Check if 'dst' is blocked waiting for this message.
* If AMF_NOREPLY is set, do not satisfy the receiving part of * If AMF_NOREPLY is set, do not satisfy the receiving part of
* a SENDREC. * a SENDREC.
*/ */
if (WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) && if (r == OK && WILLRECEIVE(dst_ptr, caller_ptr->p_endpoint) &&
(!(flags & AMF_NOREPLY) || (!(flags&AMF_NOREPLY) || !(dst_ptr->p_misc_flags&MF_REPLY_PEND))) {
!(dst_ptr->p_misc_flags & MF_REPLY_PEND)))
{
/* Destination is indeed waiting for this message. */ /* Destination is indeed waiting for this message. */
/* Copy message from sender. */ dst_ptr->p_delivermsg = tabent.msg;
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_delivermsg.m_source = caller_ptr->p_endpoint;
dst_ptr->p_misc_flags |= MF_DELIVERMSG; dst_ptr->p_misc_flags |= MF_DELIVERMSG;
IPC_STATUS_ADD_CALL(dst_ptr, SENDA); IPC_STATUS_ADD_CALL(dst_ptr, SENDA);
RTS_UNSET(dst_ptr, RTS_RECEIVING); RTS_UNSET(dst_ptr, RTS_RECEIVING);
tabent.result = OK; } else if (r == 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 */ /* Should inform receiver that something is pending */
dst_ptr->p_misc_flags |= MF_ASYNMSG; 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; done = FALSE;
continue;
} }
}
if (do_notify) if (do_notify) printf("mini_senda: should notify caller\n"); /* XXX: ? */
printf("mini_senda: should notify caller\n");
if (!done) if (!done) {
{
privp->s_asyntab = (vir_bytes) table; privp->s_asyntab = (vir_bytes) table;
privp->s_asynsize = size; privp->s_asynsize = size;
} }
return OK;
return(OK);
} }
@ -1215,8 +1167,7 @@ struct proc *caller_ptr;
int postponed = FALSE; int postponed = FALSE;
/* Try all privilege structures */ /* Try all privilege structures */
for (privp = BEG_PRIV_ADDR; privp < END_PRIV_ADDR; ++privp) for (privp = BEG_PRIV_ADDR; privp < END_PRIV_ADDR; ++privp) {
{
if (privp->s_proc_nr == NONE) if (privp->s_proc_nr == NONE)
continue; continue;
@ -1225,14 +1176,14 @@ struct proc *caller_ptr;
assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG));
r = try_one(src_ptr, caller_ptr, &postponed); r = try_one(src_ptr, caller_ptr, &postponed);
if (r == OK) if (r == OK)
return r; return(r);
} }
/* Nothing found, clear MF_ASYNMSG unless messages were postponed */ /* Nothing found, clear MF_ASYNMSG unless messages were postponed */
if (postponed == FALSE) if (postponed == FALSE)
caller_ptr->p_misc_flags &= ~MF_ASYNMSG; 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) PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr, int *postponed)
{ {
int i, done; int r = EAGAIN, i, done, do_notify, pending_recv = FALSE;
unsigned flags; unsigned flags;
size_t size; size_t size;
endpoint_t dst_e; endpoint_t dst;
struct proc *caller_ptr;
struct priv *privp; struct priv *privp;
asynmsg_t tabent; asynmsg_t tabent;
vir_bytes table_v; vir_bytes table_v;
struct proc *caller_ptr;
privp = priv(src_ptr); privp = priv(src_ptr);
if (!(privp->s_flags & SYS_PROC)) return(EPERM);
/* 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; size = privp->s_asynsize;
table_v = privp->s_asyntab; 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; caller_ptr = src_ptr;
dst_e= dst_ptr->p_endpoint;
/* Scan the table */ /* Scan the table */
do_notify = FALSE; /* XXX: this doesn't do anything? */
done = TRUE; done = TRUE;
for (i= 0; i<size; i++) for (i = 0; i < size; i++) {
{ /* Process each entry in the table and store the result in the table.
/* Read status word */ * If we're done handling a message, copy the result to the sender.
A_RETRIEVE(i, flags); * 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; flags = tabent.flags;
dst = tabent.dst;
/* Skip empty entries */ if (flags == 0) continue; /* Skip empty entries */
if (flags == 0)
{
continue;
}
/* Check for reserved bits in the flags field */ /* 'flags' field must contain only valid bits */
if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY) || if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY))
!(flags & AMF_VALID)) return(EINVAL);
{ if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */
printf("try_one: bad bits in table\n"); if (flags & AMF_DONE) continue; /* Already done processing */
privp->s_asynsize= 0;
return EINVAL;
}
/* Skip entry is AMF_DONE is already set */ /* Clear done flag. The sender is done sending when all messages in the
if (flags & AMF_DONE) * 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
continue; * send additional asynchronous messages and manages to deliver them
} * all.
/* Clear done. We are done when all entries are either empty
* or done at the start of the call.
*/ */
done = FALSE; done = FALSE;
/* Get destination */ /* Message must be directed at receiving end */
A_RETRIEVE(i, dst); if (dst != dst_ptr->p_endpoint) continue;
if (tabent.dst != dst_e) /* 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
continue; * SENDREC. This message is to be delivered later.
}
/* 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) && if ((flags & AMF_NOREPLY) && (dst_ptr->p_misc_flags & MF_REPLY_PEND)) {
(dst_ptr->p_misc_flags & MF_REPLY_PEND))
{
if (postponed != NULL) if (postponed != NULL)
*postponed = TRUE; *postponed = TRUE;
continue; continue;
} }
/* Deliver message */ /* Destination is ready to receive the message; deliver it */
A_RETRIEVE(i, msg);
dst_ptr->p_delivermsg = tabent.msg; dst_ptr->p_delivermsg = tabent.msg;
dst_ptr->p_delivermsg.m_source = src_ptr->p_endpoint; dst_ptr->p_delivermsg.m_source = src_ptr->p_endpoint;
dst_ptr->p_misc_flags |= MF_DELIVERMSG; dst_ptr->p_misc_flags |= MF_DELIVERMSG;
/* Store results for sender */
tabent.result = OK; tabent.result = OK;
A_INSERT(i, result);
tabent.flags = flags | AMF_DONE; tabent.flags = flags | AMF_DONE;
A_INSERT(i, flags); if (flags & AMF_NOTIFY) do_notify = TRUE; /* XXX: ? */
A_INSRT(i); /* Copy results to sender */
if (flags & AMF_NOTIFY) r = OK;
{ break;
printf("try_one: should notify caller\n");
} }
return OK;
if (do_notify) printf("mini_senda: should notify caller\n"); /* XXX: ? */
if (!done) {
privp->s_asyntab = table_v;
privp->s_asynsize = size;
} }
if (done)
privp->s_asynsize= 0; return(r);
return EAGAIN;
} }
/*===========================================================================* /*===========================================================================*