49,6 → 49,7 |
#include <debug.h> |
|
#include <print.h> |
#include <console/klog.h> |
#include <proc/thread.h> |
#include <arch/interrupt.h> |
#include <ipc/irq.h> |
424,33 → 425,31 |
} |
} |
|
/** Cleans up all IPC communication of the current task. |
/** Disconnects all phones connected to an answerbox. |
* |
* Note: ipc_hangup sets returning answerbox to TASK->answerbox, you |
* have to change it as well if you want to cleanup other tasks than TASK. |
* @param box Answerbox to disconnect phones from. |
* @param notify_box If true, the answerbox will get a hangup message for |
* each disconnected phone. |
*/ |
void ipc_cleanup(void) |
static void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box) |
{ |
int i; |
call_t *call; |
phone_t *phone; |
DEADLOCK_PROBE_INIT(p_phonelck); |
ipl_t ipl; |
call_t *call; |
|
/* Disconnect all our phones ('ipc_phone_hangup') */ |
for (i = 0; i < IPC_MAX_PHONES; i++) |
ipc_phone_hangup(&TASK->phones[i]); |
call = ipc_call_alloc(0); |
|
/* Disconnect all connected irqs */ |
ipc_irq_cleanup(&TASK->answerbox); |
|
/* Disconnect all phones connected to our answerbox */ |
restart_phones: |
spinlock_lock(&TASK->answerbox.lock); |
while (!list_empty(&TASK->answerbox.connected_phones)) { |
phone = list_get_instance(TASK->answerbox.connected_phones.next, |
ipl = interrupts_disable(); |
spinlock_lock(&box->lock); |
while (!list_empty(&box->connected_phones)) { |
phone = list_get_instance(box->connected_phones.next, |
phone_t, link); |
if (!spinlock_trylock(&phone->lock)) { |
spinlock_unlock(&TASK->answerbox.lock); |
spinlock_unlock(&box->lock); |
interrupts_restore(ipl); |
DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD); |
goto restart_phones; |
} |
457,13 → 456,107 |
|
/* Disconnect phone */ |
ASSERT(phone->state == IPC_PHONE_CONNECTED); |
|
list_remove(&phone->link); |
phone->state = IPC_PHONE_SLAMMED; |
list_remove(&phone->link); |
|
if (notify_box) { |
spinlock_unlock(&phone->lock); |
spinlock_unlock(&box->lock); |
interrupts_restore(ipl); |
|
/* |
* Send one message to the answerbox for each |
* phone. Used to make sure the kbox thread |
* wakes up after the last phone has been |
* disconnected. |
*/ |
IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP); |
call->flags |= IPC_CALL_DISCARD_ANSWER; |
_ipc_call(phone, box, call); |
|
/* Allocate another call in advance */ |
call = ipc_call_alloc(0); |
|
/* Must start again */ |
goto restart_phones; |
} |
|
spinlock_unlock(&phone->lock); |
} |
|
spinlock_unlock(&box->lock); |
interrupts_restore(ipl); |
|
/* Free unused call */ |
if (call) ipc_call_free(call); |
} |
|
static void ipc_kbox_cleanup() |
{ |
ipl_t ipl; |
bool have_kb_thread; |
|
/* Only hold kb_cleanup_lock while setting kb_finished - this is enough */ |
ipl = interrupts_disable(); |
spinlock_lock(&TASK->kb_cleanup_lock); |
|
TASK->kb_finished = true; |
|
spinlock_unlock(&TASK->kb_cleanup_lock); |
interrupts_restore(ipl); |
|
have_kb_thread = (TASK->kb_thread != NULL); |
|
/* From now on nobody will try to connect phones or attach kbox threads */ |
|
/* |
* Disconnect all phones connected to our kbox. Passing true for |
* notify_box causes a HANGUP message to be inserted for each |
* disconnected phone. This ensures the kbox thread is going to |
* wake up and terminate. |
*/ |
ipc_answerbox_slam_phones(&TASK->kernel_box, have_kb_thread); |
|
/* TODO: Wait for kbox thread to terminate */ |
if (have_kb_thread) { |
klog_printf("ipc_kbox_cleanup - wait for kbox thread to finish"); |
waitq_sleep(&TASK->kb_thread_shutdown_wq); |
} |
|
/* Answer all messages in 'calls' and 'dispatched_calls' queues */ |
spinlock_lock(&TASK->kernel_box.lock); |
ipc_cleanup_call_list(&TASK->kernel_box.dispatched_calls); |
ipc_cleanup_call_list(&TASK->kernel_box.calls); |
spinlock_unlock(&TASK->kernel_box.lock); |
} |
|
|
/** Cleans up all IPC communication of the current task. |
* |
* Note: ipc_hangup sets returning answerbox to TASK->answerbox, you |
* have to change it as well if you want to cleanup other tasks than TASK. |
*/ |
void ipc_cleanup(void) |
{ |
int i; |
call_t *call; |
|
/* Disconnect all our phones ('ipc_phone_hangup') */ |
for (i = 0; i < IPC_MAX_PHONES; i++) |
ipc_phone_hangup(&TASK->phones[i]); |
|
/* Disconnect all connected irqs */ |
ipc_irq_cleanup(&TASK->answerbox); |
|
/* Disconnect all phones connected to our regular answerbox */ |
ipc_answerbox_slam_phones(&TASK->answerbox, false); |
|
/* Clean up kbox thread and communications */ |
ipc_kbox_cleanup(); |
|
/* Answer all messages in 'calls' and 'dispatched_calls' queues */ |
spinlock_lock(&TASK->answerbox.lock); |
ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls); |
ipc_cleanup_call_list(&TASK->answerbox.calls); |
spinlock_unlock(&TASK->answerbox.lock); |
634,12 → 727,12 |
} |
|
if (method == IPC_M_PHONE_HUNGUP) { |
klog_printf("kbox: handle hangup message\n"); |
klog_printf("kbox: handle hangup message"); |
|
/* Was it our debugger, who hung up? */ |
if (call->sender == TASK->debugger) { |
/* Terminate debugging session (if any) */ |
klog_printf("kbox: terminate debug session\n"); |
klog_printf("kbox: terminate debug session"); |
ipl = interrupts_disable(); |
spinlock_lock(&TASK->lock); |
udebug_task_cleanup(TASK); |
646,10 → 739,10 |
spinlock_unlock(&TASK->lock); |
interrupts_restore(ipl); |
} else { |
klog_printf("kbox: was not debugger\n"); |
klog_printf("kbox: was not debugger"); |
} |
|
klog_printf("kbox: continue with hangup message\n"); |
klog_printf("kbox: continue with hangup message"); |
IPC_SET_RETVAL(call->data, 0); |
ipc_answer(&TASK->kernel_box, call); |
|
658,10 → 751,9 |
spinlock_lock(&TASK->answerbox.lock); |
if (list_empty(&TASK->answerbox.connected_phones)) { |
/* Last phone has been disconnected */ |
TASK->kb_thread_at_hand = false; |
TASK->kb_thread = NULL; |
done = true; |
printf("phone list is empty\n"); |
klog_printf("phone list is empty"); |
} |
spinlock_unlock(&TASK->answerbox.lock); |
spinlock_unlock(&TASK->lock); |
670,6 → 762,8 |
} |
} |
|
klog_printf("kbox: done, waking up possible shutdown routine"); |
waitq_wakeup(&TASK->kb_thread_shutdown_wq, WAKEUP_ALL); |
klog_printf("kbox: finished"); |
} |
|
677,6 → 771,11 |
/** |
* Connect phone to a task kernel-box specified by id. |
* |
* Note that this is not completely atomic. For optimisation reasons, |
* The task might start cleaning up kbox after the phone has been connected |
* and before a kbox thread has been created. This must be taken into account |
* in the cleanup code. |
* |
* @return Phone id on success, or negative error code. |
*/ |
int ipc_connect_kbox(task_id_t taskid) |
684,8 → 783,8 |
int newphid; |
task_t *ta; |
thread_t *kb_thread; |
int rc; |
ipl_t ipl; |
bool had_kb_thread; |
|
newphid = phone_alloc(); |
if (newphid < 0) |
697,43 → 796,85 |
ta = task_find_by_id(taskid); |
if (ta == NULL) { |
spinlock_unlock(&tasks_lock); |
interrupts_restore(ipl); |
return ENOENT; |
} |
|
spinlock_lock(&ta->lock); |
spinlock_lock(&ta->kb_cleanup_lock); |
spinlock_unlock(&tasks_lock); |
|
/* |
* Only ta->kb_cleanup_lock left. Since we checked the value |
* of ta->kb_finished, this suffices to ensure the task's exitence. |
* (And that it didn't start kbox cleanup yet). It also ensures |
* mutual exclusion with other threads running this function. |
*/ |
|
if (ta->kb_finished != false) { |
spinlock_unlock(&ta->kb_cleanup_lock); |
interrupts_restore(ipl); |
return EINVAL; |
} |
|
/* Connect the newly allocated phone to the kbox */ |
ipc_phone_connect(&TASK->phones[newphid], &ta->kernel_box); |
|
if (ta->kb_thread_at_hand == false) { |
ta->kb_thread_at_hand = true; |
spinlock_unlock(&ta->lock); |
had_kb_thread = (ta->kb_thread != NULL); |
|
/* |
* Release all locks. This is an optimisation, that makes |
* unnecessary thread creation very unlikely. |
*/ |
spinlock_unlock(&ta->kb_cleanup_lock); |
interrupts_restore(ipl); |
|
/* Create a kbox thread */ |
|
kb_thread = thread_create(kbox_thread_proc, |
NULL, ta, THREAD_FLAG_NOATTACH, "kbox", false); |
if (!kb_thread) |
return ENOMEM; |
|
rc = thread_attach_by_id(kb_thread, taskid); |
/* |
* It might happen that someone else has attached a kbox thread. |
* in the meantime. Also, the task might have gone or shut down. |
* Let's try from the beginning. |
*/ |
|
if (rc == EOK) { |
ipl = interrupts_disable(); |
spinlock_lock(&ta->lock); |
ta->kb_thread = kb_thread; |
spinlock_unlock(&ta->lock); |
spinlock_lock(&tasks_lock); |
|
ta = task_find_by_id(taskid); |
if (ta == NULL) { |
spinlock_unlock(&tasks_lock); |
return ENOENT; |
} |
|
spinlock_lock(&ta->kb_cleanup_lock); |
spinlock_unlock(&tasks_lock); |
|
if (ta->kb_finished != false || ta->kb_thread != NULL) { |
spinlock_unlock(&ta->kb_cleanup_lock); |
interrupts_restore(ipl); |
|
/* |
* Release the allocated thread struct. This won't |
* happen too often, only if two CPUs raced for |
* connecting to the kbox. |
*/ |
thread_unattached_free(kb_thread); |
return EINVAL; |
} |
|
/* Attach thread */ |
ta->kb_thread = kb_thread; |
thread_attach(kb_thread, ta); |
|
thread_detach(kb_thread); |
thread_ready(kb_thread); |
} else { |
/* Return the allocated thread struct */ |
thread_unattached_free(kb_thread); |
} |
} else { |
spinlock_unlock(&ta->lock); |
|
spinlock_unlock(&ta->kb_cleanup_lock); |
interrupts_restore(ipl); |
} |
|
return newphid; |
} |