ipc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Ondrej Palkovsky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00035 /* Lock ordering
00036  *
00037  * First the answerbox, then the phone
00038  */
00039 
00040 #include <synch/spinlock.h>
00041 #include <synch/waitq.h>
00042 #include <synch/synch.h>
00043 #include <ipc/ipc.h>
00044 #include <errno.h>
00045 #include <mm/slab.h>
00046 #include <arch.h>
00047 #include <proc/task.h>
00048 #include <memstr.h>
00049 #include <debug.h>
00050 
00051 #include <print.h>
00052 #include <proc/thread.h>
00053 #include <arch/interrupt.h>
00054 #include <ipc/irq.h>
00055 
00056 /* Open channel that is assigned automatically to new tasks */
00057 answerbox_t *ipc_phone_0 = NULL;
00058 
00059 static slab_cache_t *ipc_call_slab;
00060 
00061 /* Initialize new call */
00062 static void _ipc_call_init(call_t *call)
00063 {
00064         memsetb((__address)call, sizeof(*call), 0);
00065         call->callerbox = &TASK->answerbox;
00066         call->sender = TASK;
00067 }
00068 
00076 call_t * ipc_call_alloc(int flags)
00077 {
00078         call_t *call;
00079 
00080         call = slab_alloc(ipc_call_slab, flags);
00081         _ipc_call_init(call);
00082 
00083         return call;
00084 }
00085 
00087 void ipc_call_static_init(call_t *call)
00088 {
00089         _ipc_call_init(call);
00090         call->flags |= IPC_CALL_STATIC_ALLOC;
00091 }
00092 
00094 void ipc_call_free(call_t *call)
00095 {
00096         slab_free(ipc_call_slab, call);
00097 }
00098 
00101 void ipc_answerbox_init(answerbox_t *box)
00102 {
00103         spinlock_initialize(&box->lock, "ipc_box_lock");
00104         spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
00105         waitq_initialize(&box->wq);
00106         list_initialize(&box->connected_phones);
00107         list_initialize(&box->calls);
00108         list_initialize(&box->dispatched_calls);
00109         list_initialize(&box->answers);
00110         list_initialize(&box->irq_notifs);
00111         box->task = TASK;
00112 }
00113 
00115 void ipc_phone_connect(phone_t *phone, answerbox_t *box)
00116 {
00117         spinlock_lock(&phone->lock);
00118 
00119         phone->state = IPC_PHONE_CONNECTED;
00120         phone->callee = box;
00121 
00122         spinlock_lock(&box->lock);
00123         list_append(&phone->link, &box->connected_phones);
00124         spinlock_unlock(&box->lock);
00125 
00126         spinlock_unlock(&phone->lock);
00127 }
00128 
00131 void ipc_phone_init(phone_t *phone)
00132 {
00133         spinlock_initialize(&phone->lock, "phone_lock");
00134         phone->callee = NULL;
00135         phone->state = IPC_PHONE_FREE;
00136         atomic_set(&phone->active_calls, 0);
00137 }
00138 
00140 void ipc_call_sync(phone_t *phone, call_t *request)
00141 {
00142         answerbox_t sync_box; 
00143 
00144         ipc_answerbox_init(&sync_box);
00145 
00146         /* We will receive data on special box */
00147         request->callerbox = &sync_box;
00148 
00149         ipc_call(phone, request);
00150         ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
00151 }
00152 
00156 static void _ipc_answer_free_call(call_t *call)
00157 {
00158         answerbox_t *callerbox = call->callerbox;
00159 
00160         call->flags |= IPC_CALL_ANSWERED;
00161 
00162         spinlock_lock(&callerbox->lock);
00163         list_append(&call->link, &callerbox->answers);
00164         spinlock_unlock(&callerbox->lock);
00165         waitq_wakeup(&callerbox->wq, 0);
00166 }
00167 
00173 void ipc_answer(answerbox_t *box, call_t *call)
00174 {
00175         /* Remove from active box */
00176         spinlock_lock(&box->lock);
00177         list_remove(&call->link);
00178         spinlock_unlock(&box->lock);
00179         /* Send back answer */
00180         _ipc_answer_free_call(call);
00181 }
00182 
00188 void ipc_backsend_err(phone_t *phone, call_t *call, __native err)
00189 {
00190         call->data.phone = phone;
00191         atomic_inc(&phone->active_calls);
00192         IPC_SET_RETVAL(call->data, err);
00193         _ipc_answer_free_call(call);
00194 }
00195 
00196 /* Unsafe unchecking ipc_call */
00197 static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
00198 {
00199         if (! (call->flags & IPC_CALL_FORWARDED)) {
00200                 atomic_inc(&phone->active_calls);
00201                 call->data.phone = phone;
00202         }
00203 
00204         spinlock_lock(&box->lock);
00205         list_append(&call->link, &box->calls);
00206         spinlock_unlock(&box->lock);
00207         waitq_wakeup(&box->wq, 0);
00208 }
00209 
00215 int ipc_call(phone_t *phone, call_t *call)
00216 {
00217         answerbox_t *box;
00218 
00219         spinlock_lock(&phone->lock);
00220         if (phone->state != IPC_PHONE_CONNECTED) {
00221                 spinlock_unlock(&phone->lock);
00222                 if (call->flags & IPC_CALL_FORWARDED) {
00223                         IPC_SET_RETVAL(call->data, EFORWARD);
00224                         _ipc_answer_free_call(call);
00225                 } else {
00226                         if (phone->state == IPC_PHONE_HUNGUP)
00227                                 ipc_backsend_err(phone, call, EHANGUP);
00228                         else
00229                                 ipc_backsend_err(phone, call, ENOENT);
00230                 }
00231                 return ENOENT;
00232         }
00233         box = phone->callee;
00234         _ipc_call(phone, box, call);
00235         
00236         spinlock_unlock(&phone->lock);
00237         return 0;
00238 }
00239 
00249 int ipc_phone_hangup(phone_t *phone)
00250 {
00251         answerbox_t *box;
00252         call_t *call;
00253         
00254         spinlock_lock(&phone->lock);
00255         if (phone->state == IPC_PHONE_FREE || phone->state ==IPC_PHONE_HUNGUP \
00256             || phone->state == IPC_PHONE_CONNECTING) {
00257                 spinlock_unlock(&phone->lock);
00258                 return -1;
00259         }
00260         box = phone->callee;
00261         if (phone->state != IPC_PHONE_SLAMMED) {
00262                 /* Remove myself from answerbox */
00263                 spinlock_lock(&box->lock);
00264                 list_remove(&phone->link);
00265                 spinlock_unlock(&box->lock);
00266 
00267                 if (phone->state != IPC_PHONE_SLAMMED) {
00268                         call = ipc_call_alloc(0);
00269                         IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
00270                         call->flags |= IPC_CALL_DISCARD_ANSWER;
00271                         _ipc_call(phone, box, call);
00272                 }
00273         }
00274 
00275         phone->state = IPC_PHONE_HUNGUP;
00276         spinlock_unlock(&phone->lock);
00277 
00278         return 0;
00279 }
00280 
00291 int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
00292 {
00293         spinlock_lock(&oldbox->lock);
00294         list_remove(&call->link);
00295         spinlock_unlock(&oldbox->lock);
00296 
00297         return ipc_call(newphone, call);
00298 }
00299 
00300 
00311 call_t * ipc_wait_for_call(answerbox_t *box, __u32 usec, int flags)
00312 {
00313         call_t *request;
00314         ipl_t ipl;
00315         int rc;
00316 
00317 restart:
00318         rc = waitq_sleep_timeout(&box->wq, usec, flags);
00319         if (SYNCH_FAILED(rc))
00320                 return NULL;
00321         
00322         spinlock_lock(&box->lock);
00323         if (!list_empty(&box->irq_notifs)) {
00324                 ipl = interrupts_disable();
00325                 spinlock_lock(&box->irq_lock);
00326 
00327                 request = list_get_instance(box->irq_notifs.next, call_t, link);
00328                 list_remove(&request->link);
00329 
00330                 spinlock_unlock(&box->irq_lock);
00331                 interrupts_restore(ipl);
00332         } else if (!list_empty(&box->answers)) {
00333                 /* Handle asynchronous answers */
00334                 request = list_get_instance(box->answers.next, call_t, link);
00335                 list_remove(&request->link);
00336                 atomic_dec(&request->data.phone->active_calls);
00337         } else if (!list_empty(&box->calls)) {
00338                 /* Handle requests */
00339                 request = list_get_instance(box->calls.next, call_t, link);
00340                 list_remove(&request->link);
00341                 /* Append request to dispatch queue */
00342                 list_append(&request->link, &box->dispatched_calls);
00343         } else {
00344                 /* This can happen regularly after ipc_cleanup */
00345                 spinlock_unlock(&box->lock);
00346                 goto restart;
00347         }
00348         spinlock_unlock(&box->lock);
00349         return request;
00350 }
00351 
00353 static void ipc_cleanup_call_list(link_t *lst)
00354 {
00355         call_t *call;
00356 
00357         while (!list_empty(lst)) {
00358                 call = list_get_instance(lst->next, call_t, link);
00359                 list_remove(&call->link);
00360 
00361                 IPC_SET_RETVAL(call->data, EHANGUP);
00362                 _ipc_answer_free_call(call);
00363         }
00364 }
00365 
00371 void ipc_cleanup(void)
00372 {
00373         int i;
00374         call_t *call;
00375         phone_t *phone;
00376 
00377         /* Disconnect all our phones ('ipc_phone_hangup') */
00378         for (i=0;i < IPC_MAX_PHONES; i++)
00379                 ipc_phone_hangup(&TASK->phones[i]);
00380 
00381         /* Disconnect all connected irqs */
00382         ipc_irq_cleanup(&TASK->answerbox);
00383 
00384         /* Disconnect all phones connected to our answerbox */
00385 restart_phones:
00386         spinlock_lock(&TASK->answerbox.lock);
00387         while (!list_empty(&TASK->answerbox.connected_phones)) {
00388                 phone = list_get_instance(TASK->answerbox.connected_phones.next,
00389                                           phone_t, link);
00390                 if (! spinlock_trylock(&phone->lock)) {
00391                         spinlock_unlock(&TASK->answerbox.lock);
00392                         goto restart_phones;
00393                 }
00394                 
00395                 /* Disconnect phone */
00396                 ASSERT(phone->state == IPC_PHONE_CONNECTED);
00397                 phone->state = IPC_PHONE_SLAMMED;
00398                 list_remove(&phone->link);
00399 
00400                 spinlock_unlock(&phone->lock);
00401         }
00402 
00403         /* Answer all messages in 'calls' and 'dispatched_calls' queues */
00404         ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
00405         ipc_cleanup_call_list(&TASK->answerbox.calls);
00406         spinlock_unlock(&TASK->answerbox.lock);
00407         
00408         /* Wait for all async answers to arrive */
00409         while (1) {
00410                 /* Go through all phones, until all are FREE... */
00411                 /* Locking not needed, no one else should modify
00412                  * it, when we are in cleanup */
00413                 for (i=0;i < IPC_MAX_PHONES; i++) {
00414                         if (TASK->phones[i].state == IPC_PHONE_HUNGUP && \
00415                             atomic_get(&TASK->phones[i].active_calls) == 0)
00416                                 TASK->phones[i].state = IPC_PHONE_FREE;
00417                         
00418                         /* Just for sure, we might have had some 
00419                          * IPC_PHONE_CONNECTING phones */
00420                         if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
00421                                 ipc_phone_hangup(&TASK->phones[i]);
00422                         /* If the hangup succeeded, it has sent a HANGUP 
00423                          * message, the IPC is now in HUNGUP state, we
00424                          * wait for the reply to come */
00425                         
00426                         if (TASK->phones[i].state != IPC_PHONE_FREE)
00427                                 break;
00428                 }
00429                 /* Voila, got into cleanup */
00430                 if (i == IPC_MAX_PHONES)
00431                         break;
00432                 
00433                 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
00434                 ASSERT((call->flags & IPC_CALL_ANSWERED) || (call->flags & IPC_CALL_NOTIF));
00435                 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
00436                 
00437                 atomic_dec(&TASK->active_calls);
00438                 ipc_call_free(call);
00439         }
00440 }
00441 
00442 
00444 void ipc_init(void)
00445 {
00446         ipc_call_slab = slab_cache_create("ipc_call",
00447                                           sizeof(call_t),
00448                                           0,
00449                                           NULL, NULL, 0);
00450         ipc_irq_make_table(IRQ_COUNT);
00451 }
00452 
00453 
00455 void ipc_print_task(task_id_t taskid)
00456 {
00457         task_t *task;
00458         int i;
00459         call_t *call;
00460         link_t *tmp;
00461         
00462         spinlock_lock(&tasks_lock);
00463         task = task_find_by_id(taskid);
00464         if (task) 
00465                 spinlock_lock(&task->lock);
00466         spinlock_unlock(&tasks_lock);
00467         if (!task)
00468                 return;
00469 
00470         /* Print opened phones & details */
00471         printf("PHONE:\n");
00472         for (i=0; i < IPC_MAX_PHONES;i++) {
00473                 spinlock_lock(&task->phones[i].lock);
00474                 if (task->phones[i].state != IPC_PHONE_FREE) {
00475                         printf("%d: ",i);
00476                         switch (task->phones[i].state) {
00477                         case IPC_PHONE_CONNECTING:
00478                                 printf("connecting ");
00479                                 break;
00480                         case IPC_PHONE_CONNECTED:
00481                                 printf("connected to: %p ", 
00482                                        task->phones[i].callee);
00483                                 break;
00484                         case IPC_PHONE_SLAMMED:
00485                                 printf("slammed by: %p ", 
00486                                        task->phones[i].callee);
00487                                 break;
00488                         case IPC_PHONE_HUNGUP:
00489                                 printf("hung up - was: %p ", 
00490                                        task->phones[i].callee);
00491                                 break;
00492                         default:
00493                                 break;
00494                         }
00495                         printf("active: %d\n", atomic_get(&task->phones[i].active_calls));
00496                 }
00497                 spinlock_unlock(&task->phones[i].lock);
00498         }
00499 
00500 
00501         /* Print answerbox - calls */
00502         spinlock_lock(&task->answerbox.lock);
00503         printf("ABOX - CALLS:\n");
00504         for (tmp=task->answerbox.calls.next; tmp != &task->answerbox.calls;tmp = tmp->next) {
00505                 call = list_get_instance(tmp, call_t, link);
00506                 printf("Callid: %p Srctask:%lld M:%d A1:%d A2:%d A3:%d Flags:%x\n",call,
00507                        call->sender->taskid, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
00508                        IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), call->flags);
00509         }
00510         /* Print answerbox - calls */
00511         printf("ABOX - DISPATCHED CALLS:\n");
00512         for (tmp=task->answerbox.dispatched_calls.next; 
00513              tmp != &task->answerbox.dispatched_calls; 
00514              tmp = tmp->next) {
00515                 call = list_get_instance(tmp, call_t, link);
00516                 printf("Callid: %p Srctask:%lld M:%d A1:%d A2:%d A3:%d Flags:%x\n",call,
00517                        call->sender->taskid, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
00518                        IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), call->flags);
00519         }
00520         /* Print answerbox - calls */
00521         printf("ABOX - ANSWERS:\n");
00522         for (tmp=task->answerbox.answers.next; tmp != &task->answerbox.answers; tmp = tmp->next) {
00523                 call = list_get_instance(tmp, call_t, link);
00524                 printf("Callid:%p M:%d A1:%d A2:%d A3:%d Flags:%x\n",call,
00525                        IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
00526                        IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), call->flags);
00527         }
00528 
00529         spinlock_unlock(&task->answerbox.lock);
00530         spinlock_unlock(&task->lock);
00531 }
00532 

Generated on Sun Jun 18 17:28:03 2006 for HelenOS Kernel (ppc64) by  doxygen 1.4.6