Subversion Repositories HelenOS

Rev

Rev 2330 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2007 Jan Hudecek
  3.  * Copyright (c) 2006 Jakub Jermar
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * - Redistributions of source code must retain the above copyright
  11.  *   notice, this list of conditions and the following disclaimer.
  12.  * - Redistributions in binary form must reproduce the above copyright
  13.  *   notice, this list of conditions and the following disclaimer in the
  14.  *   documentation and/or other materials provided with the distribution.
  15.  * - The name of the author may not be used to endorse or promote products
  16.  *   derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. /** @addtogroup genericddi
  31.  * @{
  32.  */
  33. /** @file tasklet.c
  34.  *  @brief Tasklet implementation
  35.  */
  36.  
  37. #include <arch.h>
  38. #include <config.h>
  39. #include <arch/types.h>
  40. #include <arch/asm.h>
  41. #include <proc/tasklet.h>
  42. #include <synch/spinlock.h>
  43. #include <time/delay.h>
  44. #include <proc/thread.h>
  45. #include <panic.h>
  46. #include <print.h>
  47. #include <synch/waitq.h>
  48. #include <cpu.h>
  49. #include <proc/scheduler.h>
  50.  
  51. /** Timeout length in the tasklet thread */
  52. #define TASKLET_THREAD_SLEEPDELAY 10000
  53. ///** Priority of the tasklet thread */
  54. #define TASKLET_THREAD_PRIORITY 1
  55.  
  56.  
  57. /** Tasklet state enum */
  58. typedef enum  {
  59. TASKLET_STATE_NOTACTIVE = 0,
  60. TASKLET_STATE_SCHEDULED = 1,
  61. TASKLET_STATE_RUNNING = 2,
  62. TASKLET_STATE_DISABLED = 4
  63. } tasklet_state_enum;
  64.  
  65. /** Spinlock protecting list of tasklets */
  66. SPINLOCK_INITIALIZE(tasklet_lock);
  67.  
  68. /** array of tasklet lists for every CPU */
  69. tasklet_descriptor_t** tasklet_list;
  70.  
  71. /** Initializes tasklets - except for the tasklet thread */
  72. void tasklet_init(void)
  73. {
  74.     int i;
  75.     tasklet_list=malloc(sizeof(tasklet_descriptor_t*)*config.cpu_count,0);
  76.     if (tasklet_list==0)
  77.         panic_printf("tasklet list mem not allocated\n");
  78.     for(i=0;i<config.cpu_count;i++) {
  79.         tasklet_list[i]=0;
  80.     }
  81.     spinlock_initialize(&tasklet_lock, "tasklet_lock");
  82.  
  83. }
  84.  
  85. /**
  86. * Creates and runs the tasklet thread
  87. *
  88. *  @param kernel_task Pointer to the kernel task - for create_thread
  89. */
  90. void tasklet_run_tasklet_thread(task_t * kernel_task)
  91. {
  92.     //create the tasklet_thread, it's wired to the current cpu, we'll migrate it ourselves
  93.     thread_t* t= thread_create(&tasklet_thread, NULL, kernel_task, THREAD_FLAG_WIRED, "tasklet_thread", false);
  94.     if (t==NULL) {
  95.         panic_printf("tasklet thread not created\n");
  96.     } else {
  97.         spinlock_lock(&t->lock);   
  98.         //we'll default on the first CPU
  99.         t->cpu = &cpus[0];
  100.         t->priority = TASKLET_THREAD_PRIORITY;
  101.         spinlock_unlock(&t->lock);
  102.         thread_ready(t);
  103.     }
  104. }
  105.  
  106. /** Thread which keeps executing scheduled enabled tasklets
  107. * @param data not used
  108. */
  109. void tasklet_thread(void* data)
  110. {
  111. #ifdef CONFIG_SMP
  112.     int current_cpu, new_cpu ;
  113.     cpu_t *cpu;
  114. #endif
  115.     waitq_t  wq;
  116.     waitq_initialize(&wq);
  117.    
  118.     //the infinite loop
  119.     while (true) {
  120.         //execute any scheduled tasklets
  121.         tasklet_do();
  122. #ifdef CONFIG_SMP
  123.         //check whether other CPUs have tasklets to execute
  124.         if (config.cpu_active>1) {
  125.             current_cpu = CPU->id;
  126.             //find the first cpu with nonempty tasklet_list
  127.             for (new_cpu = (current_cpu + 1) % config.cpu_count; new_cpu!=current_cpu && tasklet_list[new_cpu]==0 ;
  128.                     new_cpu=(new_cpu + 1)% config.cpu_count);
  129.  
  130.             //if we found a CPU with unsatisfied tasklet schedule to run there. It must be active!
  131.             if (new_cpu!=current_cpu && cpus[new_cpu].active) {
  132.                 //we need to migrate this thread to CPU with id new_cpu
  133.                 cpu = &cpus[new_cpu];
  134.  
  135.                 spinlock_lock(&THREAD->lock);
  136.                 //move tasklet_thread on the new_cpu
  137.                 THREAD->cpu = cpu;
  138.                 spinlock_unlock(&THREAD->lock);
  139.             }
  140.         }
  141. #endif
  142.         waitq_sleep_timeout(&wq, (uint32_t)TASKLET_THREAD_SLEEPDELAY, 0);
  143.  
  144.  
  145.     }
  146. }
  147.  
  148. /** Initializes tasklet structure
  149. * @param func tasklet callback function to be called
  150. * @param data pointer to be passed to the tasklet function
  151. */
  152. tasklet_descriptor_t* tasklet_register(void (*func)(void* data), void* data)
  153. {
  154.     tasklet_descriptor_t* tasklet=malloc(sizeof(tasklet_descriptor_t),0);
  155.     tasklet->data = data;
  156.     tasklet->func = func;
  157.     tasklet->state = TASKLET_STATE_NOTACTIVE;
  158.     tasklet->next = 0;
  159.     return tasklet;
  160. }
  161.  
  162. /** Schedules the tasklet for execution on current CPU
  163. * @param t tasklet to be scheduled
  164. */
  165. void tasklet_schedule(tasklet_descriptor_t* t)
  166. {
  167.     tasklet_schedule_SMP(t, CPU->id);
  168. }
  169.  
  170. /** Schedules the tasklet for execution on id CPU
  171. * @param t tasklet to be scheduled
  172. * @param id CPU id on which the tasklet will be scheduled
  173. */
  174. void tasklet_schedule_SMP(tasklet_descriptor_t* t, uint32_t id)
  175. {
  176.     spinlock_lock(&tasklet_lock);
  177.     //clear notactive, running and scheduled flags
  178.     t->state &= TASKLET_STATE_DISABLED;
  179.     //set the scheduled flag
  180.     t->state |= TASKLET_STATE_SCHEDULED;
  181.     t->next=tasklet_list[id];
  182.     tasklet_list[id]=t;
  183.     spinlock_unlock(&tasklet_lock);
  184. }
  185.  
  186. /** Tasklet will not be run, even if scheduled
  187. * @param t tasklet to be disabled
  188. */
  189. void tasklet_disable(tasklet_descriptor_t* t)
  190. {
  191.     spinlock_lock(&tasklet_lock);
  192.     //set the disabled flag
  193.     t->state |= TASKLET_STATE_DISABLED;
  194.     spinlock_unlock(&tasklet_lock);
  195. }
  196.  
  197. /** Tasklet will be run if scheduled
  198. * @param t tasklet to be enabled
  199. */
  200. void tasklet_enable(tasklet_descriptor_t* t)
  201. {
  202.     spinlock_lock(&tasklet_lock);
  203.     //clear the disabled flag
  204.     t->state &= ~TASKLET_STATE_DISABLED;
  205.     spinlock_unlock(&tasklet_lock);
  206. }
  207.  
  208.  
  209.  
  210.  
  211. /** Executes scheduled enabled tasklets on current CPU
  212. * this function could be called from other parts of kernel */
  213. void tasklet_do(void)
  214. {
  215.     spinlock_lock(&tasklet_lock);
  216.     tasklet_descriptor_t* t = tasklet_list[CPU->id];
  217.     if (t) {
  218.         //empty the tasklet_list
  219.         tasklet_list[CPU->id]=0;
  220.         spinlock_unlock(&tasklet_lock);
  221.         do {
  222.             if (!(t->state & TASKLET_STATE_DISABLED)) {
  223.                 if (t->func) {
  224.                     t->state = TASKLET_STATE_RUNNING;
  225.                     t->func(t->data);
  226.                     //clear running flag, set not active
  227.                     t->state &= ~TASKLET_STATE_RUNNING;
  228.                     t->state |= TASKLET_STATE_NOTACTIVE;
  229.                 } else
  230.                     panic_printf("tasklet func NULL\n");
  231.             } else {  
  232.                 //return it back to the queue of scheduled tasklets
  233.                 spinlock_lock(&tasklet_lock);  
  234.                 t->next = tasklet_list[CPU->id];
  235.                 tasklet_list[CPU->id] = t;
  236.                 spinlock_unlock(&tasklet_lock);
  237.             }
  238.             t=t->next;
  239.         }
  240.         while (t);
  241.     }
  242.     else
  243.         spinlock_unlock(&tasklet_lock);
  244. }
  245.  
  246. /** Frees the tasklet structure when no longer needed. The function doesn't provide
  247. *   any synchronization, the caller must be sure, that the tasklet is not scheduled.
  248. *
  249. * @param tasklet to be freed
  250. */
  251. void tasklet_free(tasklet_descriptor_t* t)
  252. {
  253.     spinlock_lock(&tasklet_lock);
  254.     if (t->state == TASKLET_STATE_NOTACTIVE)
  255.         free(t);
  256.     else
  257.         panic_printf("attempting to free an active tasklet");
  258.     spinlock_unlock(&tasklet_lock);
  259. }
  260.  
  261.  
  262.