Subversion Repositories HelenOS-historic

Rev

Rev 1223 | Rev 1260 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (C) 2006 Ondrej Palkovsky
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /* Lock ordering
  30.  *
  31.  * First the answerbox, then the phone
  32.  */
  33.  
  34. #include <synch/spinlock.h>
  35. #include <synch/waitq.h>
  36. #include <ipc/ipc.h>
  37. #include <errno.h>
  38. #include <mm/slab.h>
  39. #include <arch.h>
  40. #include <proc/task.h>
  41. #include <memstr.h>
  42. #include <debug.h>
  43.  
  44. #include <print.h>
  45. #include <proc/thread.h>
  46. #include <arch/interrupt.h>
  47.  
  48. /* Open channel that is assigned automatically to new tasks */
  49. answerbox_t *ipc_phone_0 = NULL;
  50.  
  51. static slab_cache_t *ipc_call_slab;
  52.  
  53. typedef struct {
  54.     SPINLOCK_DECLARE(lock);
  55.     answerbox_t *box;
  56. } ipc_irq_t;
  57.  
  58. static ipc_irq_t *irq_conns = NULL;
  59. static int irq_conns_size;
  60.  
  61. /* Initialize new call */
  62. static void _ipc_call_init(call_t *call)
  63. {
  64.     memsetb((__address)call, sizeof(*call), 0);
  65.     call->callerbox = &TASK->answerbox;
  66.     call->sender = TASK;
  67. }
  68.  
  69. /** Allocate & initialize call structure
  70.  *
  71.  * The call is initialized, so that the reply will be directed
  72.  * to TASK->answerbox
  73.  *
  74.  * @param flags Parameters for slab_alloc (ATOMIC, etc.)
  75.  */
  76. call_t * ipc_call_alloc(int flags)
  77. {
  78.     call_t *call;
  79.  
  80.     call = slab_alloc(ipc_call_slab, flags);
  81.     _ipc_call_init(call);
  82.  
  83.     return call;
  84. }
  85.  
  86. /** Initialize allocated call */
  87. void ipc_call_static_init(call_t *call)
  88. {
  89.     _ipc_call_init(call);
  90.     call->flags |= IPC_CALL_STATIC_ALLOC;
  91. }
  92.  
  93. /** Deallocate call stracuture */
  94. void ipc_call_free(call_t *call)
  95. {
  96.     slab_free(ipc_call_slab, call);
  97. }
  98.  
  99. /** Initialize answerbox structure
  100.  */
  101. void ipc_answerbox_init(answerbox_t *box)
  102. {
  103.     spinlock_initialize(&box->lock, "ipc_box_lock");
  104.     spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
  105.     waitq_initialize(&box->wq);
  106.     list_initialize(&box->connected_phones);
  107.     list_initialize(&box->calls);
  108.     list_initialize(&box->dispatched_calls);
  109.     list_initialize(&box->answers);
  110.     list_initialize(&box->irq_notifs);
  111.     box->task = TASK;
  112. }
  113.  
  114. /** Connect phone to answerbox */
  115. void ipc_phone_connect(phone_t *phone, answerbox_t *box)
  116. {
  117.     spinlock_lock(&phone->lock);
  118.  
  119.     ASSERT(!phone->callee);
  120.     phone->busy = IPC_BUSY_CONNECTED;
  121.     phone->callee = box;
  122.  
  123.     spinlock_lock(&box->lock);
  124.     list_append(&phone->list, &box->connected_phones);
  125.     spinlock_unlock(&box->lock);
  126.  
  127.     spinlock_unlock(&phone->lock);
  128. }
  129.  
  130. /** Initialize phone structure and connect phone to answerbox
  131.  */
  132. void ipc_phone_init(phone_t *phone)
  133. {
  134.     spinlock_initialize(&phone->lock, "phone_lock");
  135.     phone->callee = NULL;
  136.     phone->busy = IPC_BUSY_FREE;
  137.     atomic_set(&phone->active_calls, 0);
  138. }
  139.  
  140. /** Helper function to facilitate synchronous calls */
  141. void ipc_call_sync(phone_t *phone, call_t *request)
  142. {
  143.     answerbox_t sync_box;
  144.  
  145.     ipc_answerbox_init(&sync_box);
  146.  
  147.     /* We will receive data on special box */
  148.     request->callerbox = &sync_box;
  149.  
  150.     ipc_call(phone, request);
  151.     ipc_wait_for_call(&sync_box, 0);
  152. }
  153.  
  154. /** Answer message that was not dispatched and is not entered in
  155.  * any queue
  156.  */
  157. static void _ipc_answer_free_call(call_t *call)
  158. {
  159.     answerbox_t *callerbox = call->callerbox;
  160.  
  161.     call->flags |= IPC_CALL_ANSWERED;
  162.  
  163.     spinlock_lock(&callerbox->lock);
  164.     list_append(&call->list, &callerbox->answers);
  165.     spinlock_unlock(&callerbox->lock);
  166.     waitq_wakeup(&callerbox->wq, 0);
  167. }
  168.  
  169. /** Answer message, that is in callee queue
  170.  *
  171.  * @param box Answerbox that is answering the message
  172.  * @param call Modified request that is being sent back
  173.  */
  174. void ipc_answer(answerbox_t *box, call_t *call)
  175. {
  176.     /* Remove from active box */
  177.     spinlock_lock(&box->lock);
  178.     list_remove(&call->list);
  179.     spinlock_unlock(&box->lock);
  180.     /* Send back answer */
  181.     _ipc_answer_free_call(call);
  182. }
  183.  
  184. /** Simulate sending back a message
  185.  *
  186.  * Most errors are better handled by forming a normal backward
  187.  * message and sending it as a normal answer.
  188.  */
  189. void ipc_backsend_err(phone_t *phone, call_t *call, __native err)
  190. {
  191.     call->data.phone = phone;
  192.     atomic_inc(&phone->active_calls);
  193.     if (phone->busy == IPC_BUSY_CONNECTED)
  194.         IPC_SET_RETVAL(call->data, EHANGUP);
  195.     else
  196.         IPC_SET_RETVAL(call->data, ENOENT);
  197.  
  198.     _ipc_answer_free_call(call);
  199. }
  200.  
  201. /* Unsafe unchecking ipc_call */
  202. static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
  203. {
  204.     if (! (call->flags & IPC_CALL_FORWARDED)) {
  205.         atomic_inc(&phone->active_calls);
  206.         call->data.phone = phone;
  207.     }
  208.  
  209.     spinlock_lock(&box->lock);
  210.     list_append(&call->list, &box->calls);
  211.     spinlock_unlock(&box->lock);
  212.     waitq_wakeup(&box->wq, 0);
  213. }
  214.  
  215. /** Send a asynchronous request using phone to answerbox
  216.  *
  217.  * @param phone Phone connected to answerbox
  218.  * @param request Request to be sent
  219.  */
  220. int ipc_call(phone_t *phone, call_t *call)
  221. {
  222.     answerbox_t *box;
  223.  
  224.     spinlock_lock(&phone->lock);
  225.  
  226.     box = phone->callee;
  227.     if (!box) {
  228.         /* Trying to send over disconnected phone */
  229.         spinlock_unlock(&phone->lock);
  230.         if (call->flags & IPC_CALL_FORWARDED) {
  231.             IPC_SET_RETVAL(call->data, EFORWARD);
  232.             _ipc_answer_free_call(call);
  233.         } else { /* Simulate sending back a message */
  234.             if (phone->busy == IPC_BUSY_CONNECTED)
  235.                 ipc_backsend_err(phone, call, EHANGUP);
  236.             else
  237.                 ipc_backsend_err(phone, call, ENOENT);
  238.         }
  239.  
  240.         return ENOENT;
  241.     }
  242.     _ipc_call(phone, box, call);
  243.    
  244.     spinlock_unlock(&phone->lock);
  245.     return 0;
  246. }
  247.  
  248. /** Disconnect phone from answerbox
  249.  *
  250.  * It is allowed to call disconnect on already disconnected phone
  251.  *
  252.  * @return 0 - phone disconnected, -1 - the phone was already disconnected
  253.  */
  254. int ipc_phone_hangup(phone_t *phone)
  255. {
  256.     answerbox_t *box;
  257.     call_t *call;
  258.    
  259.     spinlock_lock(&phone->lock);
  260.     box = phone->callee;
  261.     if (!box) {
  262.         if (phone->busy == IPC_BUSY_CONNECTING) {
  263.             spinlock_unlock(&phone->lock);
  264.             return -1;
  265.         }
  266.         /* Already disconnected phone */
  267.         phone->busy = IPC_BUSY_FREE;
  268.         spinlock_unlock(&phone->lock);
  269.         return 0;
  270.     }
  271.  
  272.     spinlock_lock(&box->lock);
  273.     list_remove(&phone->list);
  274.     phone->callee = NULL;
  275.     spinlock_unlock(&box->lock);
  276.  
  277.     call = ipc_call_alloc(0);
  278.     IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
  279.     call->flags |= IPC_CALL_DISCARD_ANSWER;
  280.     _ipc_call(phone, box, call);
  281.  
  282.     phone->busy = IPC_BUSY_FREE;
  283.  
  284.     spinlock_unlock(&phone->lock);
  285.  
  286.     return 0;
  287. }
  288.  
  289. /** Forwards call from one answerbox to a new one
  290.  *
  291.  * @param request Request to be forwarded
  292.  * @param newbox Target answerbox
  293.  * @param oldbox Old answerbox
  294.  * @return 0 on forward ok, error code, if there was error
  295.  *
  296.  * - the return value serves only as an information for the forwarder,
  297.  *   the original caller is notified automatically with EFORWARD
  298.  */
  299. int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
  300. {
  301.     spinlock_lock(&oldbox->lock);
  302.     list_remove(&call->list);
  303.     spinlock_unlock(&oldbox->lock);
  304.  
  305.     return ipc_call(newphone, call);
  306. }
  307.  
  308.  
  309. /** Wait for phone call
  310.  *
  311.  * @return Recived message address
  312.  * - to distinguish between call and answer, look at call->flags
  313.  */
  314. call_t * ipc_wait_for_call(answerbox_t *box, int flags)
  315. {
  316.     call_t *request;
  317.     ipl_t ipl;
  318.  
  319. restart:      
  320.     if (flags & IPC_WAIT_NONBLOCKING) {
  321.         if (waitq_sleep_timeout(&box->wq,0,1) == ESYNCH_WOULD_BLOCK)
  322.             return NULL;
  323.     } else
  324.         waitq_sleep(&box->wq);
  325.    
  326.     spinlock_lock(&box->lock);
  327.     if (!list_empty(&box->irq_notifs)) {
  328.         ipl = interrupts_disable();
  329.         spinlock_lock(&box->irq_lock);
  330.  
  331.         request = list_get_instance(box->answers.next, call_t, list);
  332.         list_remove(&request->list);
  333.  
  334.         spinlock_unlock(&box->irq_lock);
  335.         interrupts_restore(ipl);
  336.     } else if (!list_empty(&box->answers)) {
  337.         /* Handle asynchronous answers */
  338.         request = list_get_instance(box->answers.next, call_t, list);
  339.         list_remove(&request->list);
  340.         atomic_dec(&request->data.phone->active_calls);
  341.     } else if (!list_empty(&box->calls)) {
  342.         /* Handle requests */
  343.         request = list_get_instance(box->calls.next, call_t, list);
  344.         list_remove(&request->list);
  345.         /* Append request to dispatch queue */
  346.         list_append(&request->list, &box->dispatched_calls);
  347.     } else {
  348.         /* This can happen regularly after ipc_cleanup, remove
  349.          * the warning in the future when the IPC is
  350.          * more debugged */
  351.         printf("WARNING: Spurious IPC wakeup.\n");
  352.         spinlock_unlock(&box->lock);
  353.         goto restart;
  354.     }
  355.     spinlock_unlock(&box->lock);
  356.     return request;
  357. }
  358.  
  359. /** Answer all calls from list with EHANGUP msg */
  360. static void ipc_cleanup_call_list(link_t *lst)
  361. {
  362.     call_t *call;
  363.  
  364.     while (!list_empty(lst)) {
  365.         call = list_get_instance(lst->next, call_t, list);
  366.         list_remove(&call->list);
  367.  
  368.         IPC_SET_RETVAL(call->data, EHANGUP);
  369.         _ipc_answer_free_call(call);
  370.     }
  371. }
  372.  
  373. /** Disconnect all irq's notifications
  374.  *
  375.  * TODO: It may be better to do some linked list, so that
  376.  *       we wouldn't need to go through whole array every cleanup
  377.  */
  378. static void ipc_irq_cleanup(answerbox_t *box)
  379. {
  380.     int i;
  381.     ipl_t ipl;
  382.    
  383.     for (i=0; i < irq_conns_size; i++) {
  384.         ipl = interrupts_disable();
  385.         spinlock_lock(&irq_conns[i].lock);
  386.         if (irq_conns[i].box == box)
  387.             irq_conns[i].box = NULL;
  388.         spinlock_unlock(&irq_conns[i].lock);
  389.         interrupts_restore(ipl);
  390.     }
  391. }
  392.  
  393. /** Cleans up all IPC communication of the given task
  394.  *
  395.  *
  396.  */
  397. void ipc_cleanup(task_t *task)
  398. {
  399.     int i;
  400.     call_t *call;
  401.     phone_t *phone;
  402.    
  403.     /* Disconnect all our phones ('ipc_phone_hangup') */
  404.     for (i=0;i < IPC_MAX_PHONES; i++)
  405.         ipc_phone_hangup(&task->phones[i]);
  406.  
  407.     /* Disconnect all connected irqs */
  408.     ipc_irq_cleanup(&task->answerbox);
  409.  
  410.     /* Disconnect all phones connected to our answerbox */
  411. restart_phones:
  412.     spinlock_lock(&task->answerbox.lock);
  413.     while (!list_empty(&task->answerbox.connected_phones)) {
  414.         phone = list_get_instance(task->answerbox.connected_phones.next,
  415.                       phone_t,
  416.                       list);
  417.         if (! spinlock_trylock(&phone->lock)) {
  418.             spinlock_unlock(&task->answerbox.lock);
  419.             goto restart_phones;
  420.         }
  421.        
  422.         /* Disconnect phone */
  423.         phone->callee = NULL;
  424.         list_remove(&phone->list);
  425.  
  426.         spinlock_unlock(&phone->lock);
  427.     }
  428.  
  429.     /* Answer all messages in 'calls' and 'dispatched_calls' queues */
  430.     spinlock_lock(&task->answerbox.lock);
  431.     ipc_cleanup_call_list(&task->answerbox.dispatched_calls);
  432.     ipc_cleanup_call_list(&task->answerbox.calls);
  433.     spinlock_unlock(&task->answerbox.lock);
  434.    
  435.     /* Wait for all async answers to arrive */
  436.     while (atomic_get(&task->active_calls)) {
  437.         call = ipc_wait_for_call(&task->answerbox, 0);
  438.         ASSERT(call->flags & IPC_CALL_ANSWERED);
  439.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  440.        
  441.         atomic_dec(&task->active_calls);
  442.         ipc_call_free(call);
  443.     }
  444. }
  445.  
  446. /** Initialize table of interrupt handlers */
  447. static void ipc_irq_make_table(int irqcount)
  448. {
  449.     int i;
  450.  
  451.     irq_conns_size = irqcount;
  452.     irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
  453.     for (i=0; i < irqcount; i++) {
  454.         spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
  455.         irq_conns[i].box = NULL;
  456.     }
  457. }
  458.  
  459. void ipc_irq_unregister(answerbox_t *box, int irq)
  460. {
  461.     ipl_t ipl;
  462.  
  463.     ipl = interrupts_disable();
  464.     spinlock_lock(&irq_conns[irq].lock);
  465.     if (irq_conns[irq].box == box)
  466.         irq_conns[irq].box = NULL;
  467.  
  468.     spinlock_unlock(&irq_conns[irq].lock);
  469.     interrupts_restore(ipl);
  470. }
  471.  
  472. /** Register an answerbox as a receiving end of interrupts notifications */
  473. int ipc_irq_register(answerbox_t *box, int irq)
  474. {
  475.     ipl_t ipl;
  476.  
  477.     ASSERT(irq_conns);
  478.  
  479.     ipl = interrupts_disable();
  480.     spinlock_lock(&irq_conns[irq].lock);
  481.  
  482.     if (irq_conns[irq].box) {
  483.         spinlock_unlock(&irq_conns[irq].lock);
  484.         interrupts_restore(ipl);
  485.         return EEXISTS;
  486.     }
  487.     irq_conns[irq].box = box;
  488.     spinlock_unlock(&irq_conns[irq].lock);
  489.     interrupts_restore(ipl);
  490.  
  491.     return 0;
  492. }
  493.  
  494. /** Notify process that an irq had happend
  495.  *
  496.  * We expect interrupts to be disabled
  497.  */
  498. void ipc_irq_send_notif(int irq)
  499. {
  500.     call_t *call;
  501.  
  502.     ASSERT(irq_conns);
  503.     spinlock_lock(&irq_conns[irq].lock);
  504.  
  505.     if (irq_conns[irq].box) {
  506.         call = ipc_call_alloc(FRAME_ATOMIC);
  507.         call->flags |= IPC_CALL_NOTIF;
  508.         IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
  509.         IPC_SET_ARG1(call->data, irq);
  510.  
  511.         spinlock_lock(&irq_conns[irq].box->irq_lock);
  512.         list_append(&call->list, &irq_conns[irq].box->irq_notifs);
  513.         spinlock_unlock(&irq_conns[irq].box->irq_lock);
  514.  
  515.         waitq_wakeup(&irq_conns[irq].box->wq, 0);
  516.     }
  517.        
  518.     spinlock_unlock(&irq_conns[irq].lock);
  519. }
  520.  
  521. /** Initilize ipc subsystem */
  522. void ipc_init(void)
  523. {
  524.     ipc_call_slab = slab_cache_create("ipc_call",
  525.                       sizeof(call_t),
  526.                       0,
  527.                       NULL, NULL, 0);
  528.     ipc_irq_make_table(IRQ_COUNT);
  529. }
  530.  
  531.