/kernel/trunk/generic/src/ipc/ipcrsc.c |
---|
37,7 → 37,59 |
* - find phone in slot and send a message using phone |
* - answer message to phone |
* |
* Locking strategy |
* |
* - To use a phone, disconnect a phone etc., the phone must be |
* first locked and then checked that it is connected |
* - To connect an allocated phone it need not be locked (assigning |
* pointer is atomic on all platforms) |
* |
* - To find an empty phone slot, the TASK must be locked |
* - To answer a message, the answerbox must be locked |
* - The locking of phone and answerbox is done at the ipc_ level. |
* 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 |
* 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 |
* |
* Because the answerbox destroyal is much less frequent operation, |
* the first method is chosen. |
* |
* Cleanup strategy |
* |
* 1) Disconnect all phones. |
* * 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 |
* 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. |
* |
* |
*/ |
#include <synch/spinlock.h> |
58,25 → 110,6 |
return (call_t *) callid; |
} |
/** Return pointer to phone identified by phoneid or NULL if non-existent */ |
phone_t * get_phone_and_lock(__native phoneid) |
{ |
phone_t *phone; |
if (phoneid >= IPC_MAX_PHONES) |
return NULL; |
phone = &TASK->phones[phoneid]; |
spinlock_lock(&phone->lock); |
if (!phone->callee) { |
spinlock_unlock(&phone->lock); |
return NULL; |
} |
/* TODO... */ |
spinlock_unlock(&phone->lock); |
return phone; |
} |
/** Allocate new phone slot in current TASK structure */ |
int phone_alloc(void) |
{ |
111,9 → 144,18 |
spinlock_unlock(&TASK->lock); |
} |
/** Connect phone to a given answerbox |
* |
* @param phoneid The slot that will be connected |
* |
* The procedure _enforces_ that the user first marks the phone |
* busy (e.g. via phone_alloc) and then connects the phone, otherwise |
* race condition may appear. |
*/ |
void phone_connect(int phoneid, answerbox_t *box) |
{ |
phone_t *phone = &TASK->phones[phoneid]; |
ASSERT(phone->busy); |
ipc_phone_connect(phone, box); |
} |
/kernel/trunk/generic/src/ipc/sysipc.c |
---|
47,6 → 47,12 |
* |
*/ |
#define GET_CHECK_PHONE(phone,phoneid,err) { \ |
if (phoneid > IPC_MAX_PHONES) { err; } \ |
phone = &TASK->phones[phoneid]; \ |
} |
/** Return true if the method is a system method */ |
static inline int is_system_method(__native method) |
{ |
150,11 → 156,9 |
if (is_system_method(method)) |
return EPERM; |
phone = get_phone_and_lock(phoneid); |
if (!phone) |
return ENOENT; |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
ipc_call_init(&call); |
ipc_call_static_init(&call); |
IPC_SET_METHOD(call.data, method); |
IPC_SET_ARG1(call.data, arg1); |
172,15 → 176,13 |
call_t call; |
phone_t *phone; |
ipc_call_init(&call); |
ipc_call_static_init(&call); |
copy_from_uspace(&call.data, question, sizeof(call.data)); |
if (is_system_method(IPC_GET_METHOD(call.data))) |
return EPERM; |
phone = get_phone_and_lock(phoneid); |
if (!phone) |
return ENOENT; |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
ipc_call_sync(phone, &call); |
219,9 → 221,7 |
if (check_call_limit()) |
return IPC_CALLRET_TEMPORARY; |
phone = get_phone_and_lock(phoneid); |
if (!phone) |
return IPC_CALLRET_FATAL; |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
call = ipc_call_alloc(); |
IPC_SET_METHOD(call->data, method); |
245,9 → 245,7 |
if (check_call_limit()) |
return IPC_CALLRET_TEMPORARY; |
phone = get_phone_and_lock(phoneid); |
if (!phone) |
return IPC_CALLRET_FATAL; |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
call = ipc_call_alloc(); |
copy_from_uspace(&call->data, data, sizeof(call->data)); |
279,12 → 277,11 |
if (!call) |
return ENOENT; |
phone = get_phone_and_lock(phoneid); |
if (!phone) { |
GET_CHECK_PHONE(phone, phoneid, { |
IPC_SET_RETVAL(call->data, EFORWARD); |
ipc_answer(&TASK->answerbox, call); |
return ENOENT; |
} |
}); |
if (!is_forwardable(IPC_GET_METHOD(call->data))) { |
IPC_SET_RETVAL(call->data, EFORWARD); |
303,7 → 300,7 |
IPC_SET_ARG1(call->data, arg1); |
} |
ipc_forward(call, phone->callee, &TASK->answerbox); |
ipc_forward(call, phone, &TASK->answerbox); |
return 0; |
} |
371,14 → 368,12 |
call_t call; |
phone_t *phone; |
ipc_call_init(&call); |
ipc_call_static_init(&call); |
IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME); |
IPC_SET_ARG1(call.data, arg1); |
IPC_SET_ARG2(call.data, arg2); |
phone = get_phone_and_lock(phoneid); |
if (!phone) |
return ENOENT; |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
ipc_call_sync(phone, &call); |
401,15 → 396,13 |
phone_t *phone; |
int newphid; |
phone = get_phone_and_lock(phoneid); |
if (!phone) |
return ENOENT; |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
newphid = phone_alloc(); |
if (newphid < 0) |
return ELIMIT; |
ipc_call_init(&call); |
ipc_call_static_init(&call); |
IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO); |
IPC_SET_ARG1(call.data, arg1); |
IPC_SET_ARG2(call.data, arg2); |
/kernel/trunk/generic/src/ipc/ipc.c |
---|
49,6 → 49,14 |
static slab_cache_t *ipc_call_slab; |
/* Initialize new call */ |
static void _ipc_call_init(call_t *call) |
{ |
memsetb((__address)call, sizeof(*call), 0); |
call->callerbox = &TASK->answerbox; |
call->sender = TASK; |
} |
/** Allocate & initialize call structure |
* |
* The call is initialized, so that the reply will be directed |
59,19 → 67,16 |
call_t *call; |
call = slab_alloc(ipc_call_slab, 0); |
memsetb((__address)call, sizeof(*call), 0); |
call->callerbox = &TASK->answerbox; |
call->sender = TASK; |
_ipc_call_init(call); |
return call; |
} |
/** Initialize allocated call */ |
void ipc_call_init(call_t *call) |
void ipc_call_static_init(call_t *call) |
{ |
call->callerbox = &TASK->answerbox; |
call->flags = IPC_CALL_STATIC_ALLOC; |
call->sender = TASK; |
_ipc_call_init(call); |
call->flags |= IPC_CALL_STATIC_ALLOC; |
} |
/** Deallocate call stracuture */ |
96,6 → 101,8 |
/** Connect phone to answerbox */ |
void ipc_phone_connect(phone_t *phone, answerbox_t *box) |
{ |
spinlock_lock(&phone->lock); |
ASSERT(!phone->callee); |
phone->busy = 1; |
phone->callee = box; |
103,6 → 110,8 |
spinlock_lock(&box->lock); |
list_append(&phone->list, &box->connected_phones); |
spinlock_unlock(&box->lock); |
spinlock_unlock(&phone->lock); |
} |
/** Initialize phone structure and connect phone to naswerbox |
114,7 → 123,10 |
phone->busy = 0; |
} |
/** Disconnect phone from answerbox */ |
/** 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; |
121,9 → 133,16 |
ASSERT(box); |
spinlock_lock(&phone->lock); |
spinlock_lock(&box->lock); |
list_remove(&phone->list); |
if (phone->callee) { |
list_remove(&phone->list); |
phone->callee = NULL; |
} |
spinlock_unlock(&box->lock); |
spinlock_unlock(&phone->lock); |
} |
/** Helper function to facilitate synchronous calls */ |
140,6 → 159,37 |
ipc_wait_for_call(&sync_box, 0); |
} |
/** Answer message that was not dispatched and is not entered in |
* any queue |
*/ |
static void _ipc_answer_free_call(call_t *call) |
{ |
answerbox_t *callerbox = call->callerbox; |
call->flags &= ~IPC_CALL_DISPATCHED; |
call->flags |= IPC_CALL_ANSWERED; |
spinlock_lock(&callerbox->lock); |
list_append(&call->list, &callerbox->answers); |
spinlock_unlock(&callerbox->lock); |
waitq_wakeup(&callerbox->wq, 0); |
} |
/** Answer message, that is in callee queue |
* |
* @param box Answerbox that is answering the message |
* @param call Modified request that is being sent back |
*/ |
void ipc_answer(answerbox_t *box, call_t *call) |
{ |
/* Remove from active box */ |
spinlock_lock(&box->lock); |
list_remove(&call->list); |
spinlock_unlock(&box->lock); |
/* Send back answer */ |
_ipc_answer_free_call(call); |
} |
/** Send a asynchronous request using phone to answerbox |
* |
* @param phone Phone connected to answerbox |
147,14 → 197,23 |
*/ |
void ipc_call(phone_t *phone, call_t *call) |
{ |
answerbox_t *box = phone->callee; |
answerbox_t *box; |
ASSERT(box); |
spinlock_lock(&phone->lock); |
box = phone->callee; |
if (!box) { |
/* Trying to send over disconnected phone */ |
IPC_SET_RETVAL(call->data, ENOENT); |
_ipc_answer_free_call(call); |
return; |
} |
spinlock_lock(&box->lock); |
list_append(&call->list, &box->calls); |
spinlock_unlock(&box->lock); |
waitq_wakeup(&box->wq, 0); |
spinlock_unlock(&phone->lock); |
} |
/** Forwards call from one answerbox to a new one |
163,40 → 222,16 |
* @param newbox Target answerbox |
* @param oldbox Old answerbox |
*/ |
void ipc_forward(call_t *call, answerbox_t *newbox, answerbox_t *oldbox) |
void ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox) |
{ |
spinlock_lock(&oldbox->lock); |
list_remove(&call->list); |
spinlock_unlock(&oldbox->lock); |
spinlock_lock(&newbox->lock); |
list_append(&call->list, &newbox->calls); |
spinlock_lock(&newbox->lock); |
waitq_wakeup(&newbox->wq, 0); |
ipc_call(newphone, call); |
} |
/** Answer message back to phone |
* |
* @param box Answerbox that is answering the message |
* @param request Modified request that is being sent back |
*/ |
void ipc_answer(answerbox_t *box, call_t *request) |
{ |
answerbox_t *callerbox = request->callerbox; |
request->flags &= ~IPC_CALL_DISPATCHED; |
request->flags |= IPC_CALL_ANSWERED; |
spinlock_lock(&box->lock); |
list_remove(&request->list); |
spinlock_unlock(&box->lock); |
spinlock_lock(&callerbox->lock); |
list_append(&request->list, &callerbox->answers); |
spinlock_unlock(&callerbox->lock); |
waitq_wakeup(&callerbox->wq, 0); |
} |
/** Wait for phone call |
* |
* @return Recived message address |