Slightly faster IPC

- there are cycles wasted in the IPC call due to a fairly compliacted
  way of copying messages from userland to kernel. Sometimes this
  complicated way (generic though) is used even for copying within the
  kernel address space, sometimes it is used for copying in case _no_
  copying is necessary. The goal of this patch is to improve this a
  little bit.

- the places where a copy is from user to kernel use the
  copy_msg_from_user() kernel-kernel copies are turned into
  assignments and BuildNotifyMessage uses the delivery buffers to
  avoid copying.

- copy_msg_from_user() was introduced when removing the system task
  and is about 2/3 faster then using the current mechanism
  (phys_copy). It also avoids the PHYS_COPY_CATCH macro. Assignment is
  also faster and no copy is the fastest ;-) so perhaps there will be
  some hardly noticable performance gain besides the clean up.
This commit is contained in:
Tomas Hruby 2010-03-29 11:16:37 +00:00
parent b4cf88a04f
commit 2521cc6bdf

View file

@ -77,47 +77,6 @@ FORWARD _PROTOTYPE( void enqueue_head, (struct proc *rp));
break; \
}
/*===========================================================================*
* QueueMess *
*===========================================================================*/
PRIVATE int QueueMess(endpoint_t ep, vir_bytes msg_lin, struct proc *dst)
{
int k;
phys_bytes addr;
NOREC_ENTER(queuemess);
/* Queue a message from the src process (in memory) to the dst
* process (using dst process table entry). Do actual copy to
* kernel here; it's an error if the copy fails into kernel.
*/
assert(!(dst->p_misc_flags & MF_DELIVERMSG));
assert(dst->p_delivermsg_lin);
assert(isokendpt(ep, &k));
#if 0
if(INMEMORY(dst)) {
PHYS_COPY_CATCH(msg_lin, dst->p_delivermsg_lin,
sizeof(message), addr);
if(!addr) {
PHYS_COPY_CATCH(vir2phys(&ep), dst->p_delivermsg_lin,
sizeof(ep), addr);
if(!addr) {
NOREC_RETURN(queuemess, OK);
}
}
}
#endif
PHYS_COPY_CATCH(msg_lin, vir2phys(&dst->p_delivermsg), sizeof(message), addr);
if(addr) {
NOREC_RETURN(queuemess, EFAULT);
}
dst->p_delivermsg.m_source = ep;
dst->p_misc_flags |= MF_DELIVERMSG;
NOREC_RETURN(queuemess, OK);
}
/*===========================================================================*
* idle *
*===========================================================================*/
@ -561,26 +520,7 @@ const int flags;
*/
register struct proc *dst_ptr;
register struct proc **xpp;
struct proc *msg_proc_ptr; /* Proc in which the message can be found */
int dst_p;
phys_bytes linaddr;
vir_bytes addr;
int r;
/* The kernel can send messages on behalf of other processes. In that case
we need to pass a pointer to the kernel, not the caller_ptr, to
umap_local as the kernel contains the message to be sent. */
if (flags & FROM_KERNEL) {
msg_proc_ptr = proc_addr(KERNEL);
}
else {
msg_proc_ptr = caller_ptr;
}
if(!(linaddr = umap_local(msg_proc_ptr, D, (vir_bytes) m_ptr,
sizeof(message)))) {
return EFAULT;
}
dst_p = _ENDPOINT_P(dst_e);
dst_ptr = proc_addr(dst_p);
@ -596,8 +536,15 @@ const int flags;
int call;
/* Destination is indeed waiting for this message. */
assert(!(dst_ptr->p_misc_flags & MF_DELIVERMSG));
if((r=QueueMess(caller_ptr->p_endpoint, linaddr, dst_ptr)) != OK)
return r;
if (!(flags & FROM_KERNEL)) {
if(copy_msg_from_user(caller_ptr, m_ptr, &dst_ptr->p_delivermsg))
return EFAULT;
} else
dst_ptr->p_delivermsg = *m_ptr;
dst_ptr->p_delivermsg.m_source = caller_ptr->p_endpoint;
dst_ptr->p_misc_flags |= MF_DELIVERMSG;
call = (caller_ptr->p_misc_flags & MF_REPLY_PEND ? SENDREC
: (flags & NON_BLOCKING ? SENDNB : SEND));
IPC_STATUS_ADD(dst_ptr, IPC_STATUS_CALL_TO(call));
@ -613,10 +560,12 @@ const int flags;
}
/* Destination is not waiting. Block and dequeue caller. */
PHYS_COPY_CATCH(linaddr, vir2phys(&caller_ptr->p_sendmsg),
sizeof(message), addr);
if (!(flags & FROM_KERNEL)) {
if(copy_msg_from_user(caller_ptr, m_ptr, &caller_ptr->p_sendmsg))
return EFAULT;
} else
caller_ptr->p_sendmsg = *m_ptr;
if(addr) { return EFAULT; }
RTS_SET(caller_ptr, RTS_SENDING);
caller_ptr->p_sendto_e = dst_e;
@ -643,7 +592,6 @@ const int flags;
* is available block the caller.
*/
register struct proc **xpp;
message m;
sys_map_t *map;
bitchunk_t *chunk;
int i, r, src_id, src_proc_nr, src_p;
@ -699,15 +647,18 @@ const int flags;
*chunk &= ~(1 << i); /* no longer pending */
/* Found a suitable source, deliver the notification message. */
BuildNotifyMessage(&m, src_proc_nr, caller_ptr); /* assemble message */
hisep = proc_addr(src_proc_nr)->p_endpoint;
assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG));
assert(src_e == ANY || hisep == src_e);
if((r=QueueMess(hisep, vir2phys(&m), caller_ptr)) != OK) {
panic("mini_receive: local QueueMess failed");
}
/* assemble message */
BuildNotifyMessage(&caller_ptr->p_delivermsg, src_proc_nr, caller_ptr);
caller_ptr->p_delivermsg.m_source = hisep;
caller_ptr->p_misc_flags |= MF_DELIVERMSG;
IPC_STATUS_ADD(caller_ptr, IPC_STATUS_CALL_TO(NOTIFY));
return(OK); /* report success */
return(OK);
}
}
@ -735,13 +686,16 @@ const int flags;
/* Found acceptable message. Copy it and update status. */
assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG));
QueueMess((*xpp)->p_endpoint,
vir2phys(&(*xpp)->p_sendmsg), caller_ptr);
caller_ptr->p_delivermsg = (*xpp)->p_sendmsg;
caller_ptr->p_delivermsg.m_source = (*xpp)->p_endpoint;
caller_ptr->p_misc_flags |= MF_DELIVERMSG;
RTS_UNSET(*xpp, RTS_SENDING);
call = ((*xpp)->p_misc_flags & MF_REPLY_PEND ? SENDREC : SEND);
IPC_STATUS_ADD(caller_ptr, IPC_STATUS_CALL_TO(call));
if ((*xpp)->p_misc_flags & MF_SIG_DELAY)
sig_delay_done(*xpp);
RTS_UNSET(*xpp, RTS_SENDING);
*xpp = (*xpp)->p_q_link; /* remove from queue */
return(OK); /* report success */
}
@ -776,8 +730,6 @@ PUBLIC int mini_notify(
{
register struct proc *dst_ptr;
int src_id; /* source id for late delivery */
message m; /* the notification message */
int r;
int dst_p;
if (!isokendpt(dst_e, &dst_p)) {
@ -797,13 +749,15 @@ PUBLIC int mini_notify(
* message and deliver it. Copy from pseudo-source HARDWARE, since the
* message is in the kernel's address space.
*/
BuildNotifyMessage(&m, proc_nr(caller_ptr), dst_ptr);
assert(!(dst_ptr->p_misc_flags & MF_DELIVERMSG));
if((r=QueueMess(caller_ptr->p_endpoint, vir2phys(&m), dst_ptr)) != OK) {
panic("mini_notify: local QueueMess failed");
}
BuildNotifyMessage(&dst_ptr->p_delivermsg, proc_nr(caller_ptr), dst_ptr);
dst_ptr->p_delivermsg.m_source = caller_ptr->p_endpoint;
dst_ptr->p_misc_flags |= MF_DELIVERMSG;
IPC_STATUS_ADD(dst_ptr, IPC_STATUS_CALL_TO(NOTIFY));
RTS_UNSET(dst_ptr, RTS_RECEIVING);
return(OK);
}
@ -850,7 +804,6 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
struct priv *privp;
asynmsg_t tabent;
const vir_bytes table_v = (vir_bytes) table;
vir_bytes linaddr;
privp= priv(caller_ptr);
if (!(privp->s_flags & SYS_PROC))
@ -870,13 +823,6 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
return OK;
}
if(!(linaddr = umap_local(caller_ptr, D, (vir_bytes) table,
size * sizeof(*table)))) {
printf("mini_senda: umap_local failed; 0x%lx len 0x%lx\n",
table, size * sizeof(*table));
return EFAULT;
}
/* Limit size to something reasonable. An arbitrary choice is 16
* times the number of process table entries.
*
@ -985,13 +931,16 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size)
{
/* Destination is indeed waiting for this message. */
/* Copy message from sender. */
tabent.result= QueueMess(caller_ptr->p_endpoint,
linaddr + (vir_bytes) &table[i].msg -
(vir_bytes) table, dst_ptr);
if(tabent.result == OK) {
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(dst_ptr,
IPC_STATUS_CALL_TO(SENDA));
RTS_UNSET(dst_ptr, RTS_RECEIVING);
tabent.result = OK;
}
A_INSERT(i, result);
@ -1067,7 +1016,6 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr, int *postponed)
asynmsg_t tabent;
vir_bytes table_v;
struct proc *caller_ptr;
int r;
privp= priv(src_ptr);
@ -1139,10 +1087,11 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr, int *postponed)
/* Deliver message */
A_RETRIEVE(i, msg);
r = QueueMess(src_ptr->p_endpoint, vir2phys(&tabent.msg),
dst_ptr);
dst_ptr->p_delivermsg = tabent.msg;
dst_ptr->p_delivermsg.m_source = src_ptr->p_endpoint;
dst_ptr->p_misc_flags |= MF_DELIVERMSG;
tabent.result= r;
tabent.result = OK;
A_INSERT(i, result);
tabent.flags= flags | AMF_DONE;
A_INSERT(i, flags);