do_ipc() rearrangements

this patch does not add or change any functionality of do_ipc(), it
only makes things a little cleaner (hopefully).

Until now do_ipc() was responsible for handling all ipc calls. The
catch is that SENDA is fairly different which results in some ugly
code like this typecasting and variables naming which does not make
much sense for SENDA and makes the code hard to read.

result = mini_senda(caller_ptr, (asynmsg_t *)m_ptr, (size_t)src_dst_e);

As it is called directly from assembly, the new do_ipc() takes as
input values of 3 registers in reg_t variables (it used to be 4,
however, bit_map wasn't used so I removed it), does the checks common
to all ipc calls and call the appropriate handler either for
do_sync_ipc() (all except SENDA) or mini_senda() (for SENDA) while
typecasting the reg_t values correctly. As a result, handling SENDA
differences in do_sync_ipc() is no more needed. Also the code that
uses msg_size variable is improved a little bit.

arch_do_syscall() is simplified too.
This commit is contained in:
Tomas Hruby 2010-04-06 11:24:26 +00:00
parent b0d37b81c4
commit a774cc832f
4 changed files with 103 additions and 113 deletions

View file

@ -10,6 +10,7 @@
#include <minix/portio.h>
#include <minix/cpufeature.h>
#include <a.out.h>
#include <assert.h>
#include "archconst.h"
#include "proto.h"
@ -455,20 +456,11 @@ PUBLIC int arch_set_params(char *params, int size)
PUBLIC void arch_do_syscall(struct proc *proc)
{
/* Perform a previously postponed system call.
*/
int call_nr, src_dst_e;
message *m_ptr;
long bit_map;
/* Get the system call parameters from their respective registers. */
call_nr = proc->p_reg.cx;
src_dst_e = proc->p_reg.retreg;
m_ptr = (message *) proc->p_reg.bx;
bit_map = proc->p_reg.dx;
/* do_ipc assumes that it's running because of the current process */
assert(proc == proc_ptr);
/* Make the system call, for real this time. */
proc->p_reg.retreg = do_ipc(call_nr, src_dst_e, m_ptr, bit_map);
proc->p_reg.retreg =
do_ipc(proc->p_reg.cx, proc->p_reg.retreg, proc->p_reg.bx);
}
PUBLIC struct proc * arch_finish_schedcheck(void)

View file

@ -396,7 +396,6 @@ ipc_entry:
* SAVE_PROCESS_CTX() does not clobber these registers, they are still
* set as the userspace have set them
*/
push %edx
push %ebx
push %eax
push %ecx
@ -412,7 +411,7 @@ ipc_entry:
call do_ipc
/* restore the current process pointer and save the return value */
add $4 * 4, %esp
add $3 * 4, %esp
pop %esi
mov %eax, AXREG(%esi)

View file

@ -240,74 +240,25 @@ check_misc_flags:
return proc_ptr;
}
/*===========================================================================*
* sys_call *
*===========================================================================*/
PUBLIC int do_ipc(call_nr, src_dst_e, m_ptr, bit_map)
int call_nr; /* system call number and flags */
endpoint_t src_dst_e; /* src to receive from or dst to send to */
message *m_ptr; /* pointer to message in the caller's space */
long bit_map; /* notification event set or flags */
{
/* System calls are done by trapping to the kernel with an INT instruction.
* The trap is caught and sys_call() is called to send or receive a message
* (or both). The caller is always given by 'proc_ptr'.
/*
* handler for all synchronous IPC callsi
*/
struct proc *const caller_ptr = proc_ptr; /* get pointer to caller */
PUBLIC int do_sync_ipc(struct proc * caller_ptr, /* who made the call */
int call_nr, /* system call number and flags */
endpoint_t src_dst_e, /* src or dst of the call */
message *m_ptr) /* users pointer to a message */
{
int result; /* the system call's result */
int src_dst_p; /* Process slot number */
size_t msg_size;
assert(!RTS_ISSET(caller_ptr, RTS_SLOT_FREE));
/* If this process is subject to system call tracing, handle that first. */
if (caller_ptr->p_misc_flags & (MF_SC_TRACE | MF_SC_DEFER)) {
/* Are we tracing this process, and is it the first sys_call entry? */
if ((caller_ptr->p_misc_flags & (MF_SC_TRACE | MF_SC_DEFER)) ==
MF_SC_TRACE) {
/* We must notify the tracer before processing the actual
* system call. If we don't, the tracer could not obtain the
* input message. Postpone the entire system call.
*/
caller_ptr->p_misc_flags &= ~MF_SC_TRACE;
caller_ptr->p_misc_flags |= MF_SC_DEFER;
/* Signal the "enter system call" event. Block the process. */
cause_sig(proc_nr(caller_ptr), SIGTRAP);
/* Preserve the return register's value. */
return caller_ptr->p_reg.retreg;
}
/* If the MF_SC_DEFER flag is set, the syscall is now being resumed. */
caller_ptr->p_misc_flags &= ~MF_SC_DEFER;
assert (!(caller_ptr->p_misc_flags & MF_SC_ACTIVE));
/* Set a flag to allow reliable tracing of leaving the system call. */
caller_ptr->p_misc_flags |= MF_SC_ACTIVE;
}
if(caller_ptr->p_misc_flags & MF_DELIVERMSG) {
panic("sys_call: MF_DELIVERMSG on for %s / %d\n",
caller_ptr->p_name, caller_ptr->p_endpoint);
}
/* Clear IPC status code. */
IPC_STATUS_CLEAR(caller_ptr);
/* 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, SENDREC,
* and NOTIFY) require an endpoint to corresponds to a process. In addition,
* it is necessary to check whether a process is allowed to send to a given
* destination.
/* Check destination. RECEIVE is the only call that accepts ANY (in addition
* to a real endpoint). The other calls (SEND, SENDREC, and NOTIFY) require an
* endpoint to corresponds to a process. In addition, it is necessary to check
* whether a process is allowed to send to a given destination.
*/
if (call_nr == SENDA)
{
/* No destination argument */
}
else if (src_dst_e == ANY)
assert(call_nr != SENDA);
if (src_dst_e == ANY)
{
if (call_nr != RECEIVE)
{
@ -317,7 +268,7 @@ long bit_map; /* notification event set or flags */
#endif
return EINVAL;
}
src_dst_p = src_dst_e;
src_dst_p = (int) src_dst_e;
}
else
{
@ -369,10 +320,7 @@ long bit_map; /* notification event set or flags */
return(ETRAPDENIED); /* trap denied by mask or kernel */
}
/* SENDA has no src_dst value here, so this check is in mini_senda() as well.
*/
if (call_nr != SENDREC && call_nr != RECEIVE && call_nr != SENDA &&
iskerneln(src_dst_p)) {
if (call_nr != SENDREC && call_nr != RECEIVE && iskerneln(src_dst_p)) {
#if DEBUG_ENABLE_IPC_WARNINGS
printf("sys_call: trap %d not allowed, caller %d, src_dst %d\n",
call_nr, proc_nr(caller_ptr), src_dst_e);
@ -380,31 +328,6 @@ long bit_map; /* notification event set or flags */
return(ETRAPDENIED); /* trap denied by mask or kernel */
}
/* Get and check the size of the argument in bytes.
* Normally this is just the size of a regular message, but in the
* case of SENDA the argument is a table.
*/
if(call_nr == SENDA) {
msg_size = (size_t) src_dst_e;
/* Limit size to something reasonable. An arbitrary choice is 16
* times the number of process table entries.
*/
if (msg_size > 16*(NR_TASKS + NR_PROCS))
return EDOM;
msg_size *= sizeof(asynmsg_t); /* convert to bytes */
} else {
msg_size = sizeof(*m_ptr);
}
/* Now check if the call is known and try to perform the request. The only
* system calls that exist in MINIX are sending and receiving messages.
* - 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: asynchronous call; deliver notification or mark pending
* - SENDA: list of asynchronous send requests
*/
switch(call_nr) {
case SENDREC:
/* A flag is set so that notifications cannot interrupt SENDREC. */
@ -426,9 +349,6 @@ long bit_map; /* notification event set or flags */
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 */
}
@ -437,6 +357,85 @@ long bit_map; /* notification event set or flags */
return(result);
}
PUBLIC int do_ipc(reg_t r1, reg_t r2, reg_t r3)
{
struct proc * caller_ptr = proc_ptr; /* always the current process */
int call_nr = (int) r1;
assert(!RTS_ISSET(caller_ptr, RTS_SLOT_FREE));
/* If this process is subject to system call tracing, handle that first. */
if (caller_ptr->p_misc_flags & (MF_SC_TRACE | MF_SC_DEFER)) {
/* Are we tracing this process, and is it the first sys_call entry? */
if ((caller_ptr->p_misc_flags & (MF_SC_TRACE | MF_SC_DEFER)) ==
MF_SC_TRACE) {
/* We must notify the tracer before processing the actual
* system call. If we don't, the tracer could not obtain the
* input message. Postpone the entire system call.
*/
caller_ptr->p_misc_flags &= ~MF_SC_TRACE;
caller_ptr->p_misc_flags |= MF_SC_DEFER;
/* Signal the "enter system call" event. Block the process. */
cause_sig(proc_nr(caller_ptr), SIGTRAP);
/* Preserve the return register's value. */
return caller_ptr->p_reg.retreg;
}
/* If the MF_SC_DEFER flag is set, the syscall is now being resumed. */
caller_ptr->p_misc_flags &= ~MF_SC_DEFER;
assert (!(caller_ptr->p_misc_flags & MF_SC_ACTIVE));
/* Set a flag to allow reliable tracing of leaving the system call. */
caller_ptr->p_misc_flags |= MF_SC_ACTIVE;
}
if(caller_ptr->p_misc_flags & MF_DELIVERMSG) {
panic("sys_call: MF_DELIVERMSG on for %s / %d\n",
caller_ptr->p_name, caller_ptr->p_endpoint);
}
/* Clear IPC status code. */
IPC_STATUS_CLEAR(caller_ptr);
/* Now check if the call is known and try to perform the request. The only
* system calls that exist in MINIX are sending and receiving messages.
* - 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: asynchronous call; deliver notification or mark pending
* - SENDA: list of asynchronous send requests
*/
switch(call_nr) {
case SENDREC:
case SEND:
case RECEIVE:
case NOTIFY:
case SENDNB:
return do_sync_ipc(caller_ptr, call_nr, (endpoint_t) r2,
(message *) r3);
case SENDA:
{
/*
* Get and check the size of the argument in bytes as it is a
* table
*/
size_t msg_size = (size_t) r2;
/* Limit size to something reasonable. An arbitrary choice is 16
* times the number of process table entries.
*/
if (msg_size > 16*(NR_TASKS + NR_PROCS))
return EDOM;
return mini_senda(caller_ptr, (asynmsg_t *) r3, msg_size);
}
default:
return EBADCALL; /* illegal system call */
}
}
/*===========================================================================*
* deadlock *
*===========================================================================*/

View file

@ -35,8 +35,8 @@ _PROTOTYPE( void prepare_shutdown, (int how) );
_PROTOTYPE( void minix_shutdown, (struct timer *tp) );
/* proc.c */
_PROTOTYPE( int do_ipc, (int call_nr, endpoint_t src_dst,
message *m_ptr, long bit_map) );
_PROTOTYPE( int do_ipc, (reg_t r1, reg_t r2, reg_t r3) );
_PROTOTYPE( int mini_notify, (const struct proc *src, endpoint_t dst) );
_PROTOTYPE( void enqueue, (struct proc *rp) );
_PROTOTYPE( void dequeue, (const struct proc *rp) );