Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 1089 → Rev 1090

/kernel/trunk/generic/include/syscall/syscall.h
43,8 → 43,6
SYS_IPC_ANSWER,
SYS_IPC_FORWARD_FAST,
SYS_IPC_WAIT,
SYS_IPC_CONNECT_TO_ME,
SYS_IPC_CONNECT_ME_TO,
SYS_IPC_HANGUP,
SYSCALL_END
} syscall_t;
/kernel/trunk/generic/include/ipc/sysipc.h
39,12 → 39,7
__native sys_ipc_answer_fast(__native callid, __native retval,
__native arg1, __native arg2);
__native sys_ipc_answer(__native callid, ipc_data_t *data);
__native sys_ipc_connect_to_me(__native phoneid, __native arg1,
__native arg2, task_id_t *taskid);
__native sys_ipc_wait_for_call(ipc_data_t *calldata, task_id_t *taskid,
__native flags);
__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
__native arg2);
__native sys_ipc_wait_for_call(ipc_data_t *calldata, __native flags);
__native sys_ipc_forward_fast(__native callid, __native phoneid,
__native method, __native arg1);
__native sys_ipc_hangup(int phoneid);
/kernel/trunk/generic/include/ipc/ipc.h
39,10 → 39,10
/* Flags for calls */
#define IPC_CALL_ANSWERED (1<<0) /**< This is answer to a call */
#define IPC_CALL_STATIC_ALLOC (1<<1) /**< This call will not be freed on error */
#define IPC_CALL_DISPATCHED (1<<2) /**< Call is in dispatch queue */
#define IPC_CALL_DISCARD_ANSWER (1<<3) /**< Answer will not be passed to
#define IPC_CALL_DISCARD_ANSWER (1<<2) /**< Answer will not be passed to
* userspace, will be discarded */
#define IPC_CALL_FORWARDED (1<<4) /* Call was forwarded */
#define IPC_CALL_FORWARDED (1<<3) /* Call was forwarded */
#define IPC_CALL_CONN_ME_TO (1<<4) /* Identify connect_me_to */
 
/* Flags for ipc_wait_for_call */
#define IPC_WAIT_NONBLOCKING 1
182,7 → 182,9
*/
answerbox_t *callerbox;
 
ipc_data_t data;
__native private; /**< Private data to internal IPC */
 
ipc_data_t data; /**< Data passed from/to userspace */
}call_t;
 
extern void ipc_init(void);
198,11 → 200,12
extern void ipc_call_static_init(call_t *call);
extern void task_print_list(void);
extern int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox);
 
extern answerbox_t *ipc_phone_0;
extern void ipc_cleanup(task_t *task);
extern int ipc_phone_hangup(phone_t *phone);
extern void ipc_backsend_err(phone_t *phone, call_t *call, __native err);
 
extern answerbox_t *ipc_phone_0;
 
#endif
 
#endif
/kernel/trunk/generic/src/syscall/syscall.c
76,7 → 76,5
sys_ipc_answer,
sys_ipc_forward_fast,
sys_ipc_wait_for_call,
sys_ipc_connect_to_me,
sys_ipc_connect_me_to,
sys_ipc_hangup
};
/kernel/trunk/generic/src/ipc/ipcrsc.c
62,6 → 62,22
*
* Destroying is less frequent, this approach is taken.
*
* Phone call
*
* *** Connect_me_to ***
* The caller sends IPC_M_CONNECT_ME_TO to an answerbox. The server
* receives 'phoneid' of the connecting phone as an ARG3. If it answers
* with RETVAL=0, the phonecall is accepted, otherwise it is refused.
*
* *** Connect_to_me ***
* The caller sends IPC_M_CONNECT_TO_ME, with special
* The server receives an automatically
* opened phoneid. If it accepts (RETVAL=0), it can use the phoneid
* immediately.
* Possible race condition can arise, when the client receives messages
* from new connection before getting response for connect_to_me message.
* Userspace should implement handshake protocol that would control it.
*
* Phone hangup
*
* *** The caller hangs up (sys_ipc_hangup) ***
100,7 → 116,7
* 3) Answer all messages in 'calls' and 'dispatched_calls' queues with
* appropriate error code (EHANGUP, EFORWARD).
*
* 4) Wait for all async answers to arrive.
* 4) Wait for all async answers to arrive and dispose of them.
*
*/
 
142,19 → 158,22
return i;
}
 
/** Disconnect phone a free the slot
static void phone_deallocp(phone_t *phone)
{
ASSERT(phone->busy == IPC_BUSY_CONNECTING);
ASSERT(! phone->callee);
/* atomic operation */
phone->busy = IPC_BUSY_FREE;
}
 
/** Free slot from a disconnected phone
*
* All already sent messages will be correctly processed
*/
void phone_dealloc(int phoneid)
{
spinlock_lock(&TASK->lock);
 
ASSERT(TASK->phones[phoneid].busy == IPC_BUSY_CONNECTING);
ASSERT(! TASK->phones[phoneid].callee);
 
TASK->phones[phoneid].busy = IPC_BUSY_FREE;
spinlock_unlock(&TASK->lock);
phone_deallocp(&TASK->phones[phoneid]);
}
 
/** Connect phone to a given answerbox
/kernel/trunk/generic/src/ipc/sysipc.c
47,7 → 47,7
phone = &TASK->phones[phoneid]; \
}
 
#define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*src))
#define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*(src)))
 
/** Return true if the method is a system method */
static inline int is_system_method(__native method)
105,8 → 105,10
/* The connection was not accepted */
phone_dealloc(phoneid);
} else {
/* The connection was accepted */
/* The connection was accepted */
phone_connect(phoneid,&answer->sender->answerbox);
/* Set 'phone identification' as arg3 of response */
IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
/* If the users accepted call, connect */
117,6 → 119,30
}
}
 
/** Called before the request is sent
*
* @return 0 - no error, -1 - report error to user
*/
static int request_preprocess(call_t *call)
{
int newphid;
 
switch (IPC_GET_METHOD(call->data)) {
case IPC_M_CONNECT_ME_TO:
newphid = phone_alloc();
if (newphid < 0)
return ELIMIT;
/* Set arg3 for server */
IPC_SET_ARG3(call->data, (__native)&TASK->phones[newphid]);
call->flags |= IPC_CALL_CONN_ME_TO;
call->private = newphid;
break;
default:
break;
}
return 0;
}
 
/****************************************************/
/* Functions called to process received call/answer
* before passing to uspace
123,12 → 149,18
*/
 
/** Do basic kernel processing of received call answer */
static int process_answer(answerbox_t *box,call_t *call)
static void process_answer(call_t *call)
{
if (IPC_GET_RETVAL(call->data) == EHANGUP && \
call->flags & IPC_CALL_FORWARDED)
IPC_SET_RETVAL(call->data, EFORWARD);
return 0;
 
if (call->flags & IPC_CALL_CONN_ME_TO) {
if (IPC_GET_RETVAL(call->data))
phone_dealloc(call->private);
else
IPC_SET_ARG3(call->data, call->private);
}
}
 
/** Do basic kernel processing of received call request
161,18 → 193,19
{
call_t call;
phone_t *phone;
int res;
 
if (is_system_method(method))
return EPERM;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
ipc_call_static_init(&call);
IPC_SET_METHOD(call.data, method);
IPC_SET_ARG1(call.data, arg1);
ipc_call_sync(phone, &call);
 
if (!(res=request_preprocess(&call))) {
ipc_call_sync(phone, &call);
process_answer(&call);
} else
IPC_SET_RETVAL(call.data, res);
STRUCT_TO_USPACE(&data->args, &call.data.args);
 
return 0;
184,16 → 217,18
{
call_t call;
phone_t *phone;
int res;
 
ipc_call_static_init(&call);
copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
 
if (is_system_method(IPC_GET_METHOD(call.data)))
return EPERM;
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
ipc_call_sync(phone, &call);
if (!(res=request_preprocess(&call))) {
ipc_call_sync(phone, &call);
process_answer(&call);
} else
IPC_SET_RETVAL(call.data, res);
 
STRUCT_TO_USPACE(&reply->args, &call.data.args);
 
223,14 → 258,12
{
call_t *call;
phone_t *phone;
int res;
 
if (is_system_method(method))
return IPC_CALLRET_FATAL;
 
if (check_call_limit())
return IPC_CALLRET_TEMPORARY;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
 
call = ipc_call_alloc();
IPC_SET_METHOD(call->data, method);
237,7 → 270,10
IPC_SET_ARG1(call->data, arg1);
IPC_SET_ARG2(call->data, arg2);
 
ipc_call(phone, call);
if (!(res=request_preprocess(call)))
ipc_call(phone, call);
else
ipc_backsend_err(phone, call, res);
 
return (__native) call;
}
250,22 → 286,20
{
call_t *call;
phone_t *phone;
int res;
 
if (check_call_limit())
return IPC_CALLRET_TEMPORARY;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
 
call = ipc_call_alloc();
copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
if (!(res=request_preprocess(call)))
ipc_call(phone, call);
else
ipc_backsend_err(phone, call, res);
 
if (is_system_method(IPC_GET_METHOD(call->data))) {
ipc_call_free(call);
return EPERM;
}
ipc_call(phone, call);
 
return (__native) call;
}
 
304,6 → 338,9
* on forward, allow changing ARG1 and ARG2 by means of method and arg1
*/
if (is_system_method(IPC_GET_METHOD(call->data))) {
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
phone_dealloc(IPC_GET_ARG3(call->data));
 
IPC_SET_ARG1(call->data, method);
IPC_SET_ARG2(call->data, arg1);
} else {
365,68 → 402,8
return 0;
}
 
/** Ask the other side of connection to do 'callback' connection
*
* @return 0 if no error, error otherwise
*/
__native sys_ipc_connect_to_me(__native phoneid, __native arg1,
__native arg2, task_id_t *taskid)
{
call_t call;
phone_t *phone;
 
ipc_call_static_init(&call);
IPC_SET_METHOD(call.data, IPC_M_CONNECT_TO_ME);
IPC_SET_ARG1(call.data, arg1);
IPC_SET_ARG2(call.data, arg2);
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
ipc_call_sync(phone, &call);
 
if (!IPC_GET_RETVAL(call.data) && taskid)
STRUCT_TO_USPACE(taskid, &phone->callee->task->taskid);
 
return IPC_GET_RETVAL(call.data);
}
 
/** Ask target process to connect me somewhere
*
* @return phoneid - on success, error otherwise
*/
__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
__native arg2)
{
call_t call;
phone_t *phone;
int newphid;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
newphid = phone_alloc();
if (newphid < 0)
return ELIMIT;
 
ipc_call_static_init(&call);
IPC_SET_METHOD(call.data, IPC_M_CONNECT_ME_TO);
IPC_SET_ARG1(call.data, arg1);
IPC_SET_ARG2(call.data, arg2);
IPC_SET_ARG3(call.data, (__native)&TASK->phones[newphid]);
 
ipc_call_sync(phone, &call);
 
if (IPC_GET_RETVAL(call.data)) { /* Connection failed */
phone_dealloc(newphid);
return IPC_GET_RETVAL(call.data);
}
 
return newphid;
}
 
/** Hang up the phone
*
*
*
*/
__native sys_ipc_hangup(int phoneid)
{
448,9 → 425,7
* @param flags
* @return Callid, if callid & 1, then the call is answer
*/
__native sys_ipc_wait_for_call(ipc_data_t *calldata, task_id_t *taskid,
__native flags)
__native sys_ipc_wait_for_call(ipc_data_t *calldata, __native flags)
{
call_t *call;
 
460,8 → 435,7
return 0;
 
if (call->flags & IPC_CALL_ANSWERED) {
if (process_answer(&TASK->answerbox, call))
goto restart;
process_answer(call);
 
ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
481,9 → 455,8
if (process_request(&TASK->answerbox, call))
goto restart;
 
/* Include phone address('id') of the caller in the request */
/* Include phone address('id') of the caller in the request,
* copy whole call->data, not only call->data.args */
STRUCT_TO_USPACE(calldata, &call->data);
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
STRUCT_TO_USPACE(taskid, &TASK->taskid);
return (__native)call;
}
/kernel/trunk/generic/src/ipc/ipc.c
145,7 → 145,6
{
answerbox_t *callerbox = call->callerbox;
 
call->flags &= ~IPC_CALL_DISPATCHED;
call->flags |= IPC_CALL_ANSWERED;
 
spinlock_lock(&callerbox->lock);
169,6 → 168,23
_ipc_answer_free_call(call);
}
 
/** Simulate sending back a message
*
* Most errors are better handled by forming a normal backward
* message and sending it as a normal answer.
*/
void ipc_backsend_err(phone_t *phone, call_t *call, __native err)
{
call->data.phone = phone;
atomic_inc(&phone->active_calls);
if (phone->busy == IPC_BUSY_CONNECTED)
IPC_SET_RETVAL(call->data, EHANGUP);
else
IPC_SET_RETVAL(call->data, ENOENT);
 
_ipc_answer_free_call(call);
}
 
/* Unsafe unchecking ipc_call */
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
{
200,16 → 216,14
spinlock_unlock(&phone->lock);
if (call->flags & IPC_CALL_FORWARDED) {
IPC_SET_RETVAL(call->data, EFORWARD);
} else { /* Simulate sending a message */
call->data.phone = phone;
atomic_inc(&phone->active_calls);
_ipc_answer_free_call(call);
} else { /* Simulate sending back a message */
if (phone->busy == IPC_BUSY_CONNECTED)
IPC_SET_RETVAL(call->data, EHANGUP);
ipc_backsend_err(phone, call, EHANGUP);
else
IPC_SET_RETVAL(call->data, ENOENT);
ipc_backsend_err(phone, call, ENOENT);
}
 
_ipc_answer_free_call(call);
return ENOENT;
}
_ipc_call(phone, box, call);
307,7 → 321,6
list_remove(&request->list);
/* Append request to dispatch queue */
list_append(&request->list, &box->dispatched_calls);
request->flags |= IPC_CALL_DISPATCHED;
} else {
printf("WARNING: Spurious IPC wakeup.\n");
spinlock_unlock(&box->lock);