Subversion Repositories HelenOS-historic

Compare Revisions

Ignore whitespace Rev 1085 → Rev 1086

/kernel/trunk/generic/src/ipc/ipcrsc.c
36,6 → 36,8
* - disconnect connected phone (some messages might be on the fly)
* - find phone in slot and send a message using phone
* - answer message to phone
* - hangup phone (the caller has hung up)
* - hangup phone (the answerbox is exiting)
*
* Locking strategy
*
50,17 → 52,8
* It is perfectly correct to pass unconnected phone to these functions
* and proper reply will be generated.
*
* - There may be objection that a race may occur when the syscall finds
* an appropriate call and before executing ipc_send, the phone call might
* be disconnected and connected elsewhere. As there is no easy solution,
* the application will be notified by an 'PHONE_DISCONNECTED' message
* and the phone will not be allocated before the application notifies
* the kernel subsystem that it does not have any pending calls regarding
* this phone call.
*
* Locking order
*
* There are 2 possibilities
* - first phone, then answerbox
* + Easy locking on calls
* - Very hard traversing list of phones when disconnecting because
67,29 → 60,34
* the phones may disconnect during traversal of list of connected phones.
* The only possibility is try_lock with restart of list traversal.
*
* - first answerbox, then phone(s)
* + Easy phone disconnect
* - Multiple checks needed when sending message
* Destroying is less frequent, this approach is taken.
*
* Because the answerbox destroyal is much less frequent operation,
* the first method is chosen.
* Phone hangup
*
* *** The caller hangs up (sys_ipc_hangup) ***
* - The phone is disconnected (no more messages can be sent over this phone),
* all in-progress messages are correctly handled. The anwerbox receives
* IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async
* calls are answered, the phone is deallocated.
*
* *** The answerbox hangs up (ipc_answer(ESLAM))
* - The phone is disconnected. IPC_M_ANSWERBOX_HUNGUP notification
* is sent to source task, the calling process is expected to
* send an sys_ipc_hangup after cleaning up it's internal structures.
*
* Cleanup strategy
*
* 1) Disconnect all phones.
* 1) Disconnect all our phones ('sys_ipc_hangup')
*
* 2) Disconnect all phones connected to answerbox.
* * Send message 'PHONE_DISCONNECTED' to the target application
* - Once all phones are disconnected, no further calls can arrive
*
* 2) Answer all messages in 'calls' and 'dispatched_calls' queues with
* 3) Answer all messages in 'calls' and 'dispatched_calls' queues with
* appropriate error code.
*
* 3) Wait for all async answers to arrive
* Alternatively - we might try to invalidate all messages by setting some
* flag, that would dispose of the message once it is answered. This
* would need to link all calls in one big list, which we don't currently
* do.
* 4) Wait for all async answers to arrive
*
*
*/
 
#include <synch/spinlock.h>
118,7 → 116,7
spinlock_lock(&TASK->lock);
for (i=0; i < IPC_MAX_PHONES; i++) {
if (!TASK->phones[i].busy) {
if (!TASK->phones[i].busy && !atomic_get(&TASK->phones[i].active_calls)) {
TASK->phones[i].busy = 1;
break;
}
130,16 → 128,17
return i;
}
 
/** Disconnect phone */
/** Disconnect phone a free the slot
*
* All already sent messages will be correctly processed
*/
void phone_dealloc(int phoneid)
{
spinlock_lock(&TASK->lock);
 
ASSERT(TASK->phones[phoneid].busy);
ASSERT(! TASK->phones[phoneid].callee);
 
if (TASK->phones[phoneid].callee)
ipc_phone_destroy(&TASK->phones[phoneid]);
 
TASK->phones[phoneid].busy = 0;
spinlock_unlock(&TASK->lock);
}
/kernel/trunk/generic/src/ipc/sysipc.c
42,16 → 42,12
#include <arch.h>
#include <proc/thread.h>
 
/* TODO: multi-threaded connect-to-me can cause race condition
* on phone, add counter + thread_kill??
*
*/
 
#define GET_CHECK_PHONE(phone,phoneid,err) { \
if (phoneid > IPC_MAX_PHONES) { err; } \
phone = &TASK->phones[phoneid]; \
}
 
#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)
68,6 → 64,8
*/
static inline int is_forwardable(__native method)
{
if (method == IPC_M_PHONE_HUNGUP)
return 0; /* This message is meant only for the receiver */
return 1;
}
 
81,9 → 79,9
*/
static inline int answer_will_preprocess(call_t *call)
{
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
return 1;
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTMETO)
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
return 1;
return 0;
}
93,7 → 91,7
{
int phoneid;
 
if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
phoneid = IPC_GET_ARG3(*olddata);
if (IPC_GET_RETVAL(answer->data)) {
/* The connection was not accepted */
102,7 → 100,7
/* The connection was accepted */
phone_connect(phoneid,&answer->sender->answerbox);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTMETO) {
} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
/* If the users accepted call, connect */
if (!IPC_GET_RETVAL(answer->data)) {
ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
130,7 → 128,7
{
int phoneid;
 
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
phoneid = phone_alloc();
if (phoneid < 0) { /* Failed to allocate phone */
IPC_SET_RETVAL(call->data, ELIMIT);
148,7 → 146,7
-2 on 'Too many async request, handle answers first
*/
__native sys_ipc_call_sync_fast(__native phoneid, __native method,
__native arg1, __native *data)
__native arg1, ipc_data_t *data)
{
call_t call;
phone_t *phone;
164,20 → 162,20
ipc_call_sync(phone, &call);
 
copy_to_uspace(data, &call.data, sizeof(call.data));
STRUCT_TO_USPACE(&data->args, &call.data.args);
 
return 0;
}
 
/** Synchronous IPC call allowing to send whole message */
__native sys_ipc_call_sync(__native phoneid, __native *question,
__native *reply)
__native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
ipc_data_t *reply)
{
call_t call;
phone_t *phone;
 
ipc_call_static_init(&call);
copy_from_uspace(&call.data, question, sizeof(call.data));
copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
 
if (is_system_method(IPC_GET_METHOD(call.data)))
return EPERM;
186,7 → 184,7
 
ipc_call_sync(phone, &call);
 
copy_to_uspace(reply, &call.data, sizeof(call.data));
STRUCT_TO_USPACE(&reply->args, &call.data.args);
 
return 0;
}
237,7 → 235,7
*
* @return The same as sys_ipc_call_async
*/
__native sys_ipc_call_async(__native phoneid, __native *data)
__native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
{
call_t *call;
phone_t *phone;
248,7 → 246,7
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
call = ipc_call_alloc();
copy_from_uspace(&call->data, data, sizeof(call->data));
copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
 
if (is_system_method(IPC_GET_METHOD(call->data))) {
ipc_call_free(call);
334,7 → 332,7
}
 
/** Send IPC answer */
inline __native sys_ipc_answer(__native callid, __native *data)
__native sys_ipc_answer(__native callid, ipc_data_t *data)
{
call_t *call;
ipc_data_t saved_data;
348,7 → 346,8
memcpy(&saved_data, &call->data, sizeof(call->data));
preprocess = 1;
}
copy_from_uspace(&call->data, data, sizeof(call->data));
copy_from_uspace(&call->data.args, &data->args,
sizeof(call->data.args));
 
if (preprocess)
answer_preprocess(call, &saved_data);
369,7 → 368,7
phone_t *phone;
 
ipc_call_static_init(&call);
IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
IPC_SET_METHOD(call.data, IPC_M_CONNECT_TO_ME);
IPC_SET_ARG1(call.data, arg1);
IPC_SET_ARG2(call.data, arg2);
378,9 → 377,7
ipc_call_sync(phone, &call);
 
if (!IPC_GET_RETVAL(call.data) && taskid)
copy_to_uspace(taskid,
&phone->callee->task->taskid,
sizeof(TASK->taskid));
STRUCT_TO_USPACE(taskid, &phone->callee->task->taskid);
 
return IPC_GET_RETVAL(call.data);
}
403,7 → 400,7
return ELIMIT;
 
ipc_call_static_init(&call);
IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
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]);
418,19 → 415,33
return newphid;
}
 
/** Hang up the phone
*
*
*
*/
__native sys_ipc_hangup(int phoneid)
{
phone_t *phone;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
if (ipc_phone_hangup(phone))
return -1;
 
return 0;
}
 
/** Wait for incoming ipc call or answer
*
* Generic function - can serve either as inkernel or userspace call
* - inside kernel does probably unnecessary copying of data (TODO)
*
* @param result
* @param taskid
* @param calldata Pointer to buffer where the call/answer data is stored
* @param taskid On 'CONNECT_ME_TO' call it is filled with 'taskid' of
* the caller.
* @param flags
* @return Callid, if callid & 1, then the call is answer
*/
inline __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, task_id_t *taskid,
__native flags)
{
call_t *call;
442,17 → 453,27
if (process_answer(&TASK->answerbox, call))
goto restart;
 
copy_to_uspace(calldata, &call->data, sizeof(call->data));
ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
atomic_dec(&TASK->active_calls);
ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
if (call->flags & IPC_CALL_DISCARD_ANSWER) {
ipc_call_free(call);
goto restart;
}
 
STRUCT_TO_USPACE(&calldata->args, &call->data.args);
ipc_call_free(call);
 
return ((__native)call) | IPC_CALLID_ANSWERED;
}
 
if (process_request(&TASK->answerbox, call))
goto restart;
copy_to_uspace(calldata, &call->data, sizeof(call->data));
copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
 
/* Include phone address('id') of the caller in the request */
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
123,28 → 123,6
phone->busy = 0;
}
 
/** Disconnect phone from answerbox
*
* It is allowed to call disconnect on already disconnected phone\
*/
void ipc_phone_destroy(phone_t *phone)
{
answerbox_t *box = phone->callee;
ASSERT(box);
 
spinlock_lock(&phone->lock);
spinlock_lock(&box->lock);
 
if (phone->callee) {
list_remove(&phone->list);
phone->callee = NULL;
}
 
spinlock_unlock(&box->lock);
spinlock_unlock(&phone->lock);
}
 
/** Helper function to facilitate synchronous calls */
void ipc_call_sync(phone_t *phone, call_t *request)
{
190,6 → 168,18
_ipc_answer_free_call(call);
}
 
/* Unsafe unchecking ipc_call */
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
{
atomic_inc(&phone->active_calls);
call->data.phone = phone;
 
spinlock_lock(&box->lock);
list_append(&call->list, &box->calls);
spinlock_unlock(&box->lock);
waitq_wakeup(&box->wq, 0);
}
 
/** Send a asynchronous request using phone to answerbox
*
* @param phone Phone connected to answerbox
200,20 → 190,55
answerbox_t *box;
 
spinlock_lock(&phone->lock);
 
box = phone->callee;
if (!box) {
/* Trying to send over disconnected phone */
spinlock_unlock(&phone->lock);
 
call->data.phone = phone;
IPC_SET_RETVAL(call->data, ENOENT);
_ipc_answer_free_call(call);
return;
}
_ipc_call(phone, box, call);
spinlock_unlock(&phone->lock);
}
 
/** Disconnect phone from answerbox
*
* It is allowed to call disconnect on already disconnected phone
*
* @return 0 - phone disconnected, -1 - the phone was already disconnected
*/
int ipc_phone_hangup(phone_t *phone)
{
answerbox_t *box;
call_t *call;
spinlock_lock(&phone->lock);
box = phone->callee;
if (!box) {
spinlock_unlock(&phone->lock);
return -1;
}
 
spinlock_lock(&box->lock);
list_append(&call->list, &box->calls);
list_remove(&phone->list);
phone->callee = NULL;
spinlock_unlock(&box->lock);
waitq_wakeup(&box->wq, 0);
 
call = ipc_call_alloc();
IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
call->flags |= IPC_CALL_DISCARD_ANSWER;
_ipc_call(phone, box, call);
 
phone->busy = 0;
 
spinlock_unlock(&phone->lock);
 
return 0;
}
 
/** Forwards call from one answerbox to a new one
225,6 → 250,7
void ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
{
spinlock_lock(&oldbox->lock);
atomic_dec(&call->data.phone->active_calls);
list_remove(&call->list);
spinlock_unlock(&oldbox->lock);
 
241,30 → 267,32
{
call_t *request;
 
restart:
if (flags & IPC_WAIT_NONBLOCKING) {
if (waitq_sleep_timeout(&box->wq,0,1) == ESYNCH_WOULD_BLOCK)
return NULL;
} else
waitq_sleep(&box->wq);
spinlock_lock(&box->lock);
while (1) {
if (!list_empty(&box->answers)) {
/* Handle asynchronous answers */
request = list_get_instance(box->answers.next, call_t, list);
list_remove(&request->list);
} else if (!list_empty(&box->calls)) {
/* Handle requests */
request = list_get_instance(box->calls.next, call_t, list);
list_remove(&request->list);
/* Append request to dispatch queue */
list_append(&request->list, &box->dispatched_calls);
request->flags |= IPC_CALL_DISPATCHED;
} else {
if (!(flags & IPC_WAIT_NONBLOCKING)) {
/* Wait for event to appear */
spinlock_unlock(&box->lock);
waitq_sleep(&box->wq);
spinlock_lock(&box->lock);
continue;
}
request = NULL;
}
break;
if (!list_empty(&box->answers)) {
/* Handle asynchronous answers */
request = list_get_instance(box->answers.next, call_t, list);
list_remove(&request->list);
printf("%d %P\n", IPC_GET_METHOD(request->data),
request->data.phone);
atomic_dec(&request->data.phone->active_calls);
} else if (!list_empty(&box->calls)) {
/* Handle requests */
request = list_get_instance(box->calls.next, call_t, list);
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);
goto restart;
}
spinlock_unlock(&box->lock);
return request;