Subversion Repositories HelenOS

Rev

Rev 2309 | Go to most recent revision | 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
  34.  */
  35.  
  36. #include <arch.h>
  37. #include <config.h>
  38. #include <arch/types.h>
  39. #include <arch/asm.h>
  40. #include <proc/tasklet.h>
  41. #include <synch/spinlock.h>
  42. #include <time/delay.h>
  43. #include <proc/thread.h>
  44. #include <panic.h>
  45. #include <print.h>
  46. #include <synch/waitq.h>
  47. #include <cpu.h>
  48. #include <proc/scheduler.h>
  49.  
  50. /** Timeout length in the tasklet thread */
  51. #define TASKLET_THREAD_SLEEPDELAY 10000
  52. ///** Priority of the tasklet thread */
  53. #define TASKLET_THREAD_PRIORITY 1
  54.  
  55.  
  56. /** Tasklet state enum */
  57. typedef enum  {
  58. TASKLET_STATE_NOTACTIVE = 0,
  59. TASKLET_STATE_SCHEDULED = 1,
  60. TASKLET_STATE_RUNNING = 2,
  61. TASKLET_STATE_DISABLED = 4
  62. } tasklet_state_enum;
  63.  
  64. /** Spinlock protecting list of tasklets */
  65. SPINLOCK_INITIALIZE(tasklet_lock);
  66.  
  67. /** array of tasklet lists for every CPU */
  68. tasklet_descriptor_t** tasklet_list;
  69.  
  70. /** Initializes tasklets - except for the tasklet thread */
  71. void tasklet_init(void)
  72. {
  73.     int i;
  74.     tasklet_list=malloc(sizeof(tasklet_descriptor_t*)*config.cpu_count,0);
  75.     if (tasklet_list==0)
  76.         panic_printf("tasklet list mem not allocated\n");
  77.     for(i=0;i<config.cpu_count;i++) {
  78.         tasklet_list[i]=0;
  79.     }
  80.     spinlock_initialize(&tasklet_lock, "tasklet_lock");
  81.  
  82. }
  83.  
  84. /**
  85. * Creates and runs the tasklet thread
  86. *
  87. *  @param kernel_task Pointer to the kernel task - for create_thread
  88. */
  89. void tasklet_run_tasklet_thread(task_t * kernel_task)
  90. {
  91.     //create the tasklet_thread, it's wired to the current cpu, we'll migrate it ourselves
  92.     thread_t* t= thread_create(&tasklet_thread, NULL, kernel_task, THREAD_FLAG_WIRED, "tasklet_thread", false);
  93.     if (t==NULL) {
  94.         //wtf?
  95.         panic_printf("tasklet thread not created\n");
  96.     } else {
  97.         spinlock_lock(&t->lock);           
  98.         t->cpu = &cpus[0];
  99.         t->priority = TASKLET_THREAD_PRIORITY;
  100.         spinlock_unlock(&t->lock);
  101.         thread_ready(t);
  102.     }
  103. }
  104.  
  105. /** Thread which keeps executing scheduled enabled tasklets
  106. * @param data not used
  107. */
  108. void tasklet_thread(void* data)
  109. {
  110. #ifdef CONFIG_SMP
  111.     int current_cpu, new_cpu ;
  112.     cpu_t *cpu;
  113. #endif
  114.     waitq_t  wq;
  115.     waitq_initialize(&wq);
  116.    
  117.     while (true) {
  118.         tasklet_do();
  119. #ifdef CONFIG_SMP
  120.         if (config.cpu_active>1) {
  121.             current_cpu = CPU->id;
  122.             //find the first cpu with nonempty tasklet_list
  123.             for (new_cpu = (current_cpu + 1) % config.cpu_active; new_cpu!=current_cpu && tasklet_list[new_cpu]==0 && cpus[new_cpu].active;
  124.                     new_cpu=(new_cpu + 1)% config.cpu_active);
  125.  
  126.             if (new_cpu!=current_cpu) {
  127.                 //we need to migrate this thread to CPU with id new_cpu
  128.                 cpu = &cpus[new_cpu];
  129.  
  130.                 spinlock_lock(&THREAD->lock);
  131.                 //put tasklet_thread on the new_cpu
  132.                 THREAD->cpu = cpu;
  133.                 spinlock_unlock(&THREAD->lock);
  134.             }
  135.         }
  136. #endif
  137.         waitq_sleep_timeout(&wq, (uint32_t)TASKLET_THREAD_SLEEPDELAY, 0);
  138.  
  139.  
  140.     }
  141. }
  142.  
  143. /** Initializes tasklet structure
  144. * @param func tasklet callback function to be called
  145. * @param data pointer to be passed to the tasklet function
  146. */
  147. tasklet_descriptor_t* tasklet_register(void (*func)(void* data), void* data)
  148. {
  149.     tasklet_descriptor_t* tasklet=malloc(sizeof(tasklet_descriptor_t),0);
  150.     tasklet->data = data;
  151.     tasklet->func = func;
  152.     tasklet->state = TASKLET_STATE_NOTACTIVE;
  153.     tasklet->next = 0;
  154.     return tasklet;
  155. }
  156.  
  157. /** Schedules the tasklet for execution on current CPU
  158. * @param t tasklet to be scheduled
  159. */
  160. void tasklet_schedule(tasklet_descriptor_t* t)
  161. {
  162.     tasklet_schedule_SMP(t, CPU->id);
  163. }
  164.  
  165. /** Schedules the tasklet for execution on id CPU
  166. * @param t tasklet to be scheduled
  167. * @param id CPU id on which the tasklet will be scheduled
  168. */
  169. void tasklet_schedule_SMP(tasklet_descriptor_t* t, uint32_t id)
  170. {
  171.     spinlock_lock(&tasklet_lock);
  172.     //clear notactive, running and scheduled flags
  173.     t->state &= TASKLET_STATE_DISABLED;
  174.     //set the scheduled flag
  175.     t->state |= TASKLET_STATE_SCHEDULED;
  176.     t->next=tasklet_list[id];
  177.     tasklet_list[id]=t;
  178.     spinlock_unlock(&tasklet_lock);
  179. }
  180.  
  181. /** Tasklet will not be run, even if scheduled
  182. * @param t tasklet to be disabled
  183. */
  184. void tasklet_disable(tasklet_descriptor_t* t)
  185. {
  186.     spinlock_lock(&tasklet_lock);
  187.     //set the disabled flag
  188.     t->state |= TASKLET_STATE_DISABLED;
  189.     spinlock_unlock(&tasklet_lock);
  190. }
  191.  
  192. /** Tasklet will be run if scheduled
  193. * @param t tasklet to be enabled
  194. */
  195. void tasklet_enable(tasklet_descriptor_t* t)
  196. {
  197.     spinlock_lock(&tasklet_lock);
  198.     //clear the disabled flag
  199.     t->state &= ~TASKLET_STATE_DISABLED;
  200.     spinlock_unlock(&tasklet_lock);
  201. }
  202.  
  203.  
  204.  
  205.  
  206. /** Executes scheduled enabled tasklets on current CPU */
  207. void tasklet_do(void)
  208. {
  209.     spinlock_lock(&tasklet_lock);
  210.     tasklet_descriptor_t* t = tasklet_list[CPU->id];
  211.     //printf(".");
  212.     if (t) {
  213.         //empty the tasklet_list
  214.         tasklet_list[CPU->id]=0;
  215.         spinlock_unlock(&tasklet_lock);
  216.         do {
  217.             if (!(t->state & TASKLET_STATE_DISABLED)) {
  218.                 if (t->func) {
  219.                     t->state = TASKLET_STATE_RUNNING;
  220.                     t->func(t->data);
  221.                     //clear running flag, set not active - the tasklet can disable itself
  222.                     //thats why we don't just set it as not active
  223.                     t->state &= ~TASKLET_STATE_RUNNING;
  224.                     t->state |= TASKLET_STATE_NOTACTIVE;
  225.                 } else
  226.                     panic_printf("tasklet func NULL\n");
  227.             } else {  
  228.                 //return it back to the queue of scheduled tasklets
  229.                 spinlock_lock(&tasklet_lock);  
  230.                 t->next = tasklet_list[CPU->id];
  231.                 tasklet_list[CPU->id] = t;
  232.                 spinlock_unlock(&tasklet_lock);
  233.             }
  234.             t=t->next;
  235.         }
  236.         while (t);
  237.     }
  238.     else
  239.         spinlock_unlock(&tasklet_lock);
  240. }
  241.  
  242. /** Frees the tasklet structure when no longer needed. The function doesn't provide
  243. *   any synchronization, the caller must be sure, the tasklet is not scheduled.
  244. *
  245. * @param tasklet to be freed
  246. */
  247. void tasklet_free(tasklet_descriptor_t* t)
  248. {
  249.     spinlock_lock(&tasklet_lock);
  250.     if (t->state == TASKLET_STATE_NOTACTIVE)
  251.         free(t);
  252.     else
  253.         panic_printf("attempting to free an active tasklet");
  254.     spinlock_unlock(&tasklet_lock);
  255. }
  256.  
  257.  
  258.