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 /* IPC resources management 00036 * 00037 * The goal of this source code is to properly manage IPC resources 00038 * and allow straight and clean clean-up procedure upon task termination. 00039 * 00040 * The pattern of usage of the resources is: 00041 * - allocate empty phone slot, connect | deallocate slot 00042 * - disconnect connected phone (some messages might be on the fly) 00043 * - find phone in slot and send a message using phone 00044 * - answer message to phone 00045 * - hangup phone (the caller has hung up) 00046 * - hangup phone (the answerbox is exiting) 00047 * 00048 * Locking strategy 00049 * 00050 * - To use a phone, disconnect a phone etc., the phone must be 00051 * first locked and then checked that it is connected 00052 * - To connect an allocated phone it need not be locked (assigning 00053 * pointer is atomic on all platforms) 00054 * 00055 * - To find an empty phone slot, the TASK must be locked 00056 * - To answer a message, the answerbox must be locked 00057 * - The locking of phone and answerbox is done at the ipc_ level. 00058 * It is perfectly correct to pass unconnected phone to these functions 00059 * and proper reply will be generated. 00060 * 00061 * Locking order 00062 * 00063 * - first phone, then answerbox 00064 * + Easy locking on calls 00065 * - Very hard traversing list of phones when disconnecting because 00066 * the phones may disconnect during traversal of list of connected phones. 00067 * The only possibility is try_lock with restart of list traversal. 00068 * 00069 * Destroying is less frequent, this approach is taken. 00070 * 00071 * Phone call 00072 * 00073 * *** Connect_me_to *** 00074 * The caller sends IPC_M_CONNECT_ME_TO to an answerbox. The server 00075 * receives 'phoneid' of the connecting phone as an ARG3. If it answers 00076 * with RETVAL=0, the phonecall is accepted, otherwise it is refused. 00077 * 00078 * *** Connect_to_me *** 00079 * The caller sends IPC_M_CONNECT_TO_ME, with special 00080 * The server receives an automatically 00081 * opened phoneid. If it accepts (RETVAL=0), it can use the phoneid 00082 * immediately. 00083 * Possible race condition can arise, when the client receives messages 00084 * from new connection before getting response for connect_to_me message. 00085 * Userspace should implement handshake protocol that would control it. 00086 * 00087 * Phone hangup 00088 * 00089 * *** The caller hangs up (sys_ipc_hangup) *** 00090 * - The phone is disconnected (no more messages can be sent over this phone), 00091 * all in-progress messages are correctly handled. The anwerbox receives 00092 * IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async 00093 * calls are answered, the phone is deallocated. 00094 * 00095 * *** The answerbox hangs up (ipc_answer(EHANGUP)) 00096 * - The phone is disconnected. EHANGUP response code is sent 00097 * to the calling task. All new calls through this phone 00098 * get a EHUNGUP error code, the task is expected to 00099 * send an sys_ipc_hangup after cleaning up it's internal structures. 00100 * 00101 * Call forwarding 00102 * 00103 * The call can be forwarded, so that the answer to call is passed directly 00104 * to the original sender. However, this poses special problems regarding 00105 * routing of hangup messages. 00106 * 00107 * sys_ipc_hangup -> IPC_M_PHONE_HUNGUP 00108 * - this message CANNOT be forwarded 00109 * 00110 * EHANGUP during forward 00111 * - The *forwarding* phone will be closed, EFORWARD is sent to receiver. 00112 * 00113 * EHANGUP, ENOENT during forward 00114 * - EFORWARD is sent to the receiver, ipc_forward returns error code EFORWARD 00115 * 00116 * Cleanup strategy 00117 * 00118 * 1) Disconnect all our phones ('ipc_phone_hangup'). 00119 * 00120 * 2) Disconnect all phones connected to answerbox. 00121 * 00122 * 3) Answer all messages in 'calls' and 'dispatched_calls' queues with 00123 * appropriate error code (EHANGUP, EFORWARD). 00124 * 00125 * 4) Wait for all async answers to arrive and dispose of them. 00126 * 00127 */ 00128 00129 #include <synch/spinlock.h> 00130 #include <ipc/ipc.h> 00131 #include <arch.h> 00132 #include <proc/task.h> 00133 #include <ipc/ipcrsc.h> 00134 #include <debug.h> 00135 00141 call_t * get_call(__native callid) 00142 { 00143 link_t *lst; 00144 call_t *call, *result = NULL; 00145 00146 spinlock_lock(&TASK->answerbox.lock); 00147 for (lst = TASK->answerbox.dispatched_calls.next; 00148 lst != &TASK->answerbox.dispatched_calls; lst = lst->next) { 00149 call = list_get_instance(lst, call_t, link); 00150 if ((__native)call == callid) { 00151 result = call; 00152 break; 00153 } 00154 } 00155 spinlock_unlock(&TASK->answerbox.lock); 00156 return result; 00157 } 00158 00160 int phone_alloc(void) 00161 { 00162 int i; 00163 00164 spinlock_lock(&TASK->lock); 00165 00166 for (i=0; i < IPC_MAX_PHONES; i++) { 00167 if (TASK->phones[i].state == IPC_PHONE_HUNGUP && \ 00168 atomic_get(&TASK->phones[i].active_calls) == 0) 00169 TASK->phones[i].state = IPC_PHONE_FREE; 00170 00171 if (TASK->phones[i].state == IPC_PHONE_FREE) { 00172 TASK->phones[i].state = IPC_PHONE_CONNECTING; 00173 break; 00174 } 00175 } 00176 spinlock_unlock(&TASK->lock); 00177 00178 if (i >= IPC_MAX_PHONES) 00179 return -1; 00180 return i; 00181 } 00182 00183 static void phone_deallocp(phone_t *phone) 00184 { 00185 ASSERT(phone->state == IPC_PHONE_CONNECTING); 00186 00187 /* atomic operation */ 00188 phone->state = IPC_PHONE_FREE; 00189 } 00190 00195 void phone_dealloc(int phoneid) 00196 { 00197 phone_deallocp(&TASK->phones[phoneid]); 00198 } 00199 00208 void phone_connect(int phoneid, answerbox_t *box) 00209 { 00210 phone_t *phone = &TASK->phones[phoneid]; 00211 00212 ASSERT(phone->state == IPC_PHONE_CONNECTING); 00213 ipc_phone_connect(phone, box); 00214 } 00215