[BACK]Return to msg.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / ipc

Diff for /prex-old/sys/ipc/msg.c between version 1.1.1.1 and 1.1.1.1.2.1

version 1.1.1.1, 2008/06/03 10:38:46 version 1.1.1.1.2.1, 2008/08/13 17:12:31
Line 32 
Line 32 
  */   */
   
 /*  /*
    * Design:
    *
  * Messages are sent to the specific object by using msg_send().   * Messages are sent to the specific object by using msg_send().
  * The transmission of a message is completely synchronous with   * The transmission of a message is completely synchronous with
  * this kernel. This means the thread which sent a message is blocked   * this kernel. This means the thread which sent a message is
  * until it receives a response from another thread. msg_receive()   * blocked until it receives a response from another thread.
  * performs reception of a message. msg_receive() is also blocked   * msg_receive() performs reception of a message. msg_receive() is
  * when no message is reached to the target object. The receiver   * also blocked when no message is reached to the target object.
  * thread must answer the message using msg_reply() after it finishes   * The receiver thread must answer the message using msg_reply()
  * its message processing.   * after it finishes its message processing.
  *   *
  * The receiver thread can not receive another message until it   * The receiver thread can not receive another message until it
  * replies to the sender. In short, a thread can receive only one   * replies to the sender. In short, a thread can receive only one
  * message at once. Once the thread receives message, it can send   * message at once. Once the thread receives message, it can send
  * another message to different object. This mechanism allows threads   * another message to different object. This mechanism allows
  * to redirect the sender's request to another thread.   * threads to redirect the sender's request to another thread.
  *   *
  * The message is copied from thread to thread directly without any   * The message is copied from thread to thread directly without any
  * kernel buffering. If sent message contains a buffer, sender's memory   * kernel buffering. If sent message contains a buffer, sender's
  * region is automatically mapped to the receiver's memory in kernel.   * memory region is automatically mapped to the receiver's memory
  * Since there is no page out of memory in this system, we can copy the   * in kernel. Since there is no page out of memory in this system,
  * message data via physical memory at anytime.   * we can copy the message data via physical memory at anytime.
  */   */
   
 #include <kernel.h>  #include <kernel.h>
Line 67 
Line 69 
 #define min(a,b)        (((a) < (b)) ? (a) : (b))  #define min(a,b)        (((a) < (b)) ? (a) : (b))
   
 /* forward declarations */  /* forward declarations */
 static thread_t msg_dequeue(queue_t);  static thread_t msg_dequeue(queue_t);
 static void msg_enqueue(queue_t, thread_t);  static void     msg_enqueue(queue_t, thread_t);
   
 /* event for IPC operation */  /* event for IPC operation */
 static struct event ipc_event;  static struct event ipc_event;
   
 /*  /*
  * msg_send - send a message.   * Send a message.
  * @obj:  object ID to send a message.  
  * @msg:  pointer to the message buffer  
  * @size: size of the message buffer.  
  *   *
  * The current thread will be blocked until any other thread receives   * The current thread will be blocked until any other thread
  * the message and calls msg_reply() for the target object.   * receives the message and calls msg_reply() for the target
  * When new message has been reached to the object, it will be received   * object. When new message has been reached to the object, it
  * by highest priority thread waiting for that message.   * will be received by highest priority thread waiting for
  * A thread can send a message to any object if it knows the object id.   * that message. A thread can send a message to any object if
    * it knows the object id.
  */   */
 int  int
 msg_send(object_t obj, void *msg, size_t size)  msg_send(object_t obj, void *msg, size_t size)
 {  {
           struct msg_header *hdr;
         thread_t th;          thread_t th;
         void *kmsg;          void *kmsg;
         int rc;          int rc;
Line 109 
Line 110 
                 return EPERM;                  return EPERM;
         }          }
         /*          /*
          * A thread can not send a message when the thread is           * A thread can not send a message when the
          * already receiving from the target object. This will           * thread is already receiving from the target
          * obviously cause a deadlock.           * object. This will obviously cause a deadlock.
          */           */
         if (obj == cur_thread->recv_obj) {          if (obj == cur_thread->recvobj) {
                 sched_unlock();                  sched_unlock();
                 return EDEADLK;                  return EDEADLK;
         }          }
         /*          /*
          * Translate message address to the kernel linear address.           * Translate message address to the kernel linear
          * So that a receiver thread can access the message via           * address.  So that a receiver thread can access
          * kernel pointer. We can catch the page fault here.           * the message via kernel pointer. We can catch
            * the page fault here.
          */           */
         if ((kmsg = kmem_map(msg, size)) == NULL) {          if ((kmsg = kmem_map(msg, size)) == NULL) {
                 /* Error - no physical address for the message */                  /* Error - no physical address for the message */
Line 128 
Line 130 
                 return EFAULT;                  return EFAULT;
         }          }
         /*          /*
          * Fill sender task ID in the message header.           * The sender ID in the message header is filled
          * So, the receiver can trust this ID.           * by the kernel. So, the receiver can trust it.
          */           */
         ((struct msg_header *)kmsg)->task = cur_task();          hdr = (struct msg_header *)kmsg;
           hdr->task = cur_task();
   
         /* Save the data for message block. */          /* Save information about the message block. */
         cur_thread->msg_addr = kmsg;          cur_thread->msgaddr = kmsg;
         cur_thread->msg_size = size;          cur_thread->msgsize = size;
   
         /*          /*
          * If receiver already exists, wake it up. Highest priority           * If receiver already exists, wake it up.
          * thread will get this message.           * Highest priority thread will get this message.
          */           */
         if (!queue_empty(&obj->recvq)) {          if (!queue_empty(&obj->recvq)) {
                 th = msg_dequeue(&obj->recvq);                  th = msg_dequeue(&obj->recvq);
Line 147 
Line 150 
         }          }
         /*          /*
          * Sleep until we get a reply message.           * Sleep until we get a reply message.
          * Note: we can not touch the data in obj after we wakeup           * Note: Do not touch any data in the object
          * because it may be deleted during we were sleeping.           * structure after we wakeup. This is because the
            * target object may be deleted during we were
            * sleeping.
          */           */
         cur_thread->send_obj = obj;          cur_thread->sendobj = obj;
         msg_enqueue(&obj->sendq, cur_thread);          msg_enqueue(&obj->sendq, cur_thread);
         rc = sched_sleep(&ipc_event);          rc = sched_sleep(&ipc_event);
         if (rc == SLP_INTR)          if (rc == SLP_INTR)
                 queue_remove(&cur_thread->ipc_link);                  queue_remove(&cur_thread->ipc_link);
         cur_thread->send_obj = NULL;          cur_thread->sendobj = NULL;
   
         sched_unlock();          sched_unlock();
   
Line 169 
Line 174 
                 return EINVAL;  /* Object has been deleted */                  return EINVAL;  /* Object has been deleted */
         case SLP_INTR:          case SLP_INTR:
                 return EINTR;   /* Exception */                  return EINTR;   /* Exception */
           default:
                   /* DO NOTHING */
                   break;
         }          }
         return 0;          return 0;
 }  }
Line 176 
Line 184 
 /*  /*
  * Receive a message.   * Receive a message.
  *   *
  * A thread can receive a message from the object which was created   * A thread can receive a message from the object which was
  * by any thread belongs to same task. If the message has not arrived   * created by any thread belongs to same task. If the message
  * yet, it blocks until any message comes in.   * has not arrived yet, it blocks until any message comes in.
  *   *
  * The size argument specifies the "maximum" size of the message   * The size argument specifies the "maximum" size of the message
  * buffer to receive. If the sent message is larger than this size,   * buffer to receive. If the sent message is larger than this
  * the kernel will automatically clip the message to the receive buffer   * size, the kernel will automatically clip the message to the
  * size.   * receive buffer size.
  *   *
  * When message is received, the sender thread is removed from   * When message is received, the sender thread is removed from
  * object's send queue. So, another thread can receive the subsequent   * object's send queue. So, another thread can receive the
  * message from that object. This is important for the multi-thread   * subsequent message from that object. This is important for
  * server which receives some messages simultaneously.   * the multi-thread server which receives some messages
    * simultaneously.
  */   */
 int  int
 msg_receive(object_t obj, void *msg, size_t size)  msg_receive(object_t obj, void *msg, size_t size)
 {  {
         thread_t th;          thread_t th;
         int err, rc;  
         size_t len;          size_t len;
           int rc, err = 0;
   
         err = 0;  
         if (!user_area(msg))          if (!user_area(msg))
                 return EFAULT;                  return EFAULT;
   
Line 212 
Line 220 
                 goto out;                  goto out;
         }          }
         /*          /*
          * Check if this thread finished previous receive operation.           * Check if this thread finished previous receive
          * A thread can not receive different messages at once.           * operation.  A thread can not receive different
            * messages at once.
          */           */
         if (cur_thread->recv_obj) {          if (cur_thread->recvobj) {
                 err = EBUSY;                  err = EBUSY;
                 goto out;                  goto out;
         }          }
         cur_thread->recv_obj = obj;          cur_thread->recvobj = obj;
   
         /*          /*
          * If no message exists, wait until message arrives.           * If no message exists, wait until message arrives.
          */           */
         while (queue_empty(&obj->sendq)) {          while (queue_empty(&obj->sendq)) {
                 /*                  /*
                  * Sleep until message comes in.                   * Block until someone sends the message.
                  */                   */
                 msg_enqueue(&obj->recvq, cur_thread);                  msg_enqueue(&obj->recvq, cur_thread);
                 rc = sched_sleep(&ipc_event);                  rc = sched_sleep(&ipc_event);
                 if (rc == 0) {                  if (rc != 0) {
                         /*                          /*
                          * Even if this thread is woken by the sender thread,                           * Receive is failed due to some reasons.
                          * the message may be received by another thread  
                          * before this thread runs. This can occur when  
                          * higher priority thread becomes runnable at that  
                          * time. So, it is necessary to check the existence  
                          * of the sender here again. The following line must  
                          * be "continue" instead of "break" to check the  
                          * queue again.  
                          */                           */
                         continue;                          switch (rc) {
                           case SLP_INVAL:
                                   err = EINVAL;   /* Object has been deleted */
                                   break;
                           case SLP_INTR:
                                   queue_remove(&cur_thread->ipc_link);
                                   err = EINTR;    /* Got exception */
                                   break;
                           default:
                                   panic("msg_receive: invalid wake reason");
                                   break;
                           }
                           cur_thread->recvobj = NULL;
                           goto out;
                 }                  }
   
                 /*                  /*
                  * Receive is failed by some reason.                   * Even if this thread is woken by the sender thread,
                    * the message may be received by another thread
                    * before this thread runs. This can occur when
                    * higher priority thread becomes runnable at that
                    * time. So, it is necessary to check the existence
                    * of the sender, again.
                  */                   */
                 switch (rc) {  
                 case SLP_INVAL:  
                         err = EINVAL;   /* Object has been deleted */  
                         break;  
                 case SLP_INTR:  
                         queue_remove(&cur_thread->ipc_link);  
                         err = EINTR;    /* Got exception */  
                         break;  
                 default:  
                         panic("msg_receive");  
                 }  
                 cur_thread->recv_obj = NULL;  
                 goto out;  
         }          }
   
         th = msg_dequeue(&obj->sendq);          th = msg_dequeue(&obj->sendq);
   
         /*          /*
          * Copy message to user space.           * Copy out the message to the user-space.
          * The smaller buffer size is used as copy length           * The smaller buffer size is used as copy length
          * between sender and receiver thread.           * between sender and receiver thread.
          */           */
         len = min(size, th->msg_size);          len = min(size, th->msgsize);
         if (len > 0) {          if (len > 0) {
                 if (umem_copyout(th->msg_addr, msg, len)) {                  if (umem_copyout(th->msgaddr, msg, len)) {
                         msg_enqueue(&obj->sendq, th);                          msg_enqueue(&obj->sendq, th);
                         cur_thread->recv_obj = NULL;                          cur_thread->recvobj = NULL;
                         err = EFAULT;                          err = EFAULT;
                         goto out;                          goto out;
                 }                  }
Line 290 
Line 298 
 /*  /*
  * Send a reply message.   * Send a reply message.
  *   *
  * The target object must be an appropriate object that current thread   * The target object must be an appropriate object that current
  * has been received from. Otherwise, this function will be failed.   * thread has been received from. Otherwise, this function will
    * be failed.
  *   *
  * Since the target object may already be deleted, we can not access   * Since the target object may already be deleted, we can not
  * the data of the object within this routine.   * access the data of the object within this routine.
  */   */
 int  int
 msg_reply(object_t obj, void *msg, size_t size)  msg_reply(object_t obj, void *msg, size_t size)
Line 308 
Line 317 
   
         sched_lock();          sched_lock();
   
         if (!object_valid(obj) || obj != cur_thread->recv_obj) {          if (!object_valid(obj) || obj != cur_thread->recvobj) {
                 sched_unlock();                  sched_unlock();
                 return EINVAL;                  return EINVAL;
         }          }
Line 323 
Line 332 
          * Copy message to the sender's buffer.           * Copy message to the sender's buffer.
          */           */
         th = cur_thread->sender;          th = cur_thread->sender;
         len = min(size, th->msg_size);          len = min(size, th->msgsize);
         if (len > 0) {          if (len > 0) {
                 if (umem_copyin(msg, th->msg_addr, len)) {                  if (umem_copyin(msg, th->msgaddr, len)) {
                         sched_unlock();                          sched_unlock();
                         return EFAULT;                          return EFAULT;
                 }                  }
Line 338 
Line 347 
  out:   out:
         /* Clear transmit state */          /* Clear transmit state */
         cur_thread->sender = NULL;          cur_thread->sender = NULL;
         cur_thread->recv_obj = NULL;          cur_thread->recvobj = NULL;
   
         sched_unlock();          sched_unlock();
         return err;          return err;
Line 346 
Line 355 
   
 /*  /*
  * Clean up pending message operation of specified thread in order   * Clean up pending message operation of specified thread in order
  * to prevent deadlock.   * to prevent deadlock. This is called when the thread is killed.
  * This is called when the thread is killed.  
  * It is necessary to deal with the following conditions.   * It is necessary to deal with the following conditions.
  *   *
  * If killed thread is sender:   * If killed thread is sender:
Line 370 
Line 378 
   
         sched_lock();          sched_lock();
   
         if (th->send_obj) {          if (th->sendobj) {
                 if (th->receiver)                  if (th->receiver)
                         th->receiver->sender = NULL;                          th->receiver->sender = NULL;
                 else                  else
                         queue_remove(&th->ipc_link);                          queue_remove(&th->ipc_link);
         }          }
         if (th->recv_obj) {          if (th->recvobj) {
                 if (th->sender) {                  if (th->sender) {
                         sched_unsleep(th->sender, SLP_BREAK);                          sched_unsleep(th->sender, SLP_BREAK);
                         th->sender->receiver = NULL;                          th->sender->receiver = NULL;
Line 387 
Line 395 
 }  }
   
 /*  /*
  * Cancel all message operation relevant to the specified object.   * Cancel all message operation relevant to the specified
  * This is called when target object is deleted.   * object.
  * All threads in message queue are woken to avoid deadlock.   *
  * If the message has already been received, send/reply operation   * This is called when target object is deleted.  All threads
    * in message queue are woken to avoid deadlock.  If the
    * message has already been received, send/reply operation
  * continue processing normally.   * continue processing normally.
  */   */
 void  void
 msg_cancel(object_t obj)  msg_cancel(object_t obj)
 {  {
         queue_t head, q;          queue_t q;
         thread_t th;          thread_t th;
   
         sched_lock();          sched_lock();
   
         /*          /*
          * Force wakeup all thread in the send queue.           * Force wakeup all threads in the send queue.
          */           */
         head = &obj->sendq;          while (!queue_empty(&obj->sendq)) {
         for (q = queue_first(head); !queue_end(head, q); q = queue_next(q)) {                  q = dequeue(&obj->sendq);
                 th = queue_entry(q, struct thread, ipc_link);                  th = queue_entry(q, struct thread, ipc_link);
                 sched_unsleep(th, SLP_INVAL);                  sched_unsleep(th, SLP_INVAL);
         }          }
         /*          /*
          * Force wakeup all thread waiting for receive.           * Force wakeup all threads waiting for receive.
          */           */
         head = &obj->recvq;          while (!queue_empty(&obj->recvq)) {
         for (q = queue_first(head); !queue_end(head, q); q = queue_next(q)) {                  q = dequeue(&obj->sendq);
                 th = queue_entry(q, struct thread, ipc_link);                  th = queue_entry(q, struct thread, ipc_link);
                 sched_unsleep(th, SLP_INVAL);                  sched_unsleep(th, SLP_INVAL);
         }          }

Legend:
Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.1.2.1

CVSweb