1 /* 2 * mm/pdflush.c - worker threads for writing back filesystem data 3 * 4 * Copyright (C) 2002, Linus Torvalds. 5 * 6 * 09Apr2002 Andrew Morton 7 * Initial version 8 * 29Feb2004 [email protected] 9 * Move worker thread creation to kthread to avoid chewing 10 * up stack space with nested calls to kernel_thread. 11 */ 12 13 #include <linux/sched.h> 14 #include <linux/list.h> 15 #include <linux/signal.h> 16 #include <linux/spinlock.h> 17 #include <linux/gfp.h> 18 #include <linux/init.h> 19 #include <linux/module.h> 20 #include <linux/fs.h> /* Needed by writeback.h */ 21 #include <linux/writeback.h> /* Prototypes pdflush_operation() */ 22 #include <linux/kthread.h> 23 #include <linux/cpuset.h> 24 #include <linux/freezer.h> 25 26 27 /* 28 * Minimum and maximum number of pdflush instances 29 */ 30 #define MIN_PDFLUSH_THREADS 2 //最少同时存在2个工作线程 31 #define MAX_PDFLUSH_THREADS 8 //最多同时存在8个工作线程 32 33 static void start_one_pdflush_thread(void); 34 35 36 /* 37 * The pdflush threads are worker threads for writing back dirty data. 38 * Ideally, we'd like one thread per active disk spindle. But the disk 39 * topology is very hard to divine at this level. Instead, we take 40 * care in various places to prevent more than one pdflush thread from 41 * performing writeback against a single filesystem. pdflush threads 42 * have the PF_FLUSHER flag set in current->flags to aid in this. 43 */ 44 45 /* 46 * All the pdflush threads. Protected by pdflush_lock 47 */ //初始化pdflush线程描述符链表,用于保存正在睡眠的pdflush?? 48 static LIST_HEAD(pdflush_list); //pdflush_lock用来保护链表不会被并发访问 49 static DEFINE_SPINLOCK(pdflush_lock); 50 51 /* 52 * The count of currently-running pdflush threads. Protected 53 * by pdflush_lock. 54 * 55 * Readable by sysctl, but not writable. Published to userspace at 56 * /proc/sys/vm/nr_pdflush_threads. 57 */ 58 int nr_pdflush_threads = 0; //内核pdflush线程总数; 59 60 /* 61 * The time at which the pdflush thread pool last went empty 62 */ 63 static unsigned long last_empty_jifs; // 上次链表为空的时间; 64 65 /* 66 * The pdflush thread. 67 * 68 * Thread pool management algorithm: 69 * 70 * - The minimum and maximum number of pdflush instances are bound 71 * by MIN_PDFLUSH_THREADS and MAX_PDFLUSH_THREADS. 72 * 73 * - If there have been no idle pdflush instances for 1 second, create 74 * a new one. 75 * 76 * - If the least-recently-went-to-sleep pdflush thread has been asleep 77 * for more than one second, terminate a thread. 78 */ 79 80 /* 81 * A structure for passing work to a pdflush thread. Also for passing 82 * state information between pdflush threads. Protected by pdflush_lock. 83 */ 84 struct pdflush_work { 85 struct task_struct *who; /* The thread 指向线程描述符*/ 86 void (*fn)(unsigned long); /* A callback function 负责脏页下盘回调函数的指针*/ 87 unsigned long arg0; /* An argument to the callback 传给回调函数的参数*/ 88 struct list_head list; /* On pdflush_list, when idle 链表的指针*/ 89 unsigned long when_i_went_to_sleep; /* 该线程上次休眠的时间 */ 90 }; 91 92 static int __pdflush(struct pdflush_work *my_work) 93 { 94 current->flags |= PF_FLUSHER | PF_SWAPWRITE; 95 set_freezable(); //置为可冻结状态 96 my_work->fn = NULL; //先将回调函数指针置为空 97 my_work->who = current; //将线程描述符置为当前线程的current指针 98 INIT_LIST_HEAD(&my_work->list); //初始化链表 99 100 spin_lock_irq(&pdflush_lock); //加锁 101 for ( ; ; ) { 102 struct pdflush_work *pdf; 103 104 set_current_state(TASK_INTERRUPTIBLE); //将进程状态置为可中断 105 list_move(&my_work->list, &pdflush_list); //将该线程存入pdflush_list链表,准备睡眠 106 my_work->when_i_went_to_sleep = jiffies; //记录这次休眠的时间 107 spin_unlock_irq(&pdflush_lock); //解锁 108 schedule(); //将cpu交给系统来调度 109 try_to_freeze(); //检查是否要进入电源睡眠状态,并进行处理 110 spin_lock_irq(&pdflush_lock); //解锁 111 if (!list_empty(&my_work->list)) { //检查链表是否为空 112 /* 113 * Someone woke us up, but without removing our control 114 * structure from the global list. swsusp will do this 115 * in try_to_freeze()->refrigerator(). Handle it. 116 */ 117 my_work->fn = NULL; 118 continue; 119 } 120 if (my_work->fn == NULL) { //如果链表不为空,但回调函数为空,说明是假唤醒,需要继续循环 121 printk("pdflush: bogus wakeup\n"); 122 continue; 123 } 124 spin_unlock_irq(&pdflush_lock); //解锁 125 126 (*my_work->fn)(my_work->arg0); //调用回调函数写脏页,干活就指着这里了 127 128 spin_lock_irq(&pdflush_lock); //加锁 129 130 /* 131 * Thread creation: For how long have there been zero 132 * available threads? 133 * 134 * To throttle creation, we reset last_empty_jifs. 135 */ 136 if (time_after(jiffies, last_empty_jifs + 1 * HZ)) { //如果当前的时间比上次链表为空时间晚1hz的时间 137 if (list_empty(&pdflush_list)) { //如果当前pdflush_list链表为空,说明当前的已经没有正在睡眠的pdflush线程 138 if (nr_pdflush_threads < MAX_PDFLUSH_THREADS) { //如果当前的pdflush线程数小于8 139 last_empty_jifs = jiffies; //更新上次链表为空时间 140 nr_pdflush_threads++; //增加的线程数 141 spin_unlock_irq(&pdflush_lock); //加锁pdflush_lock 142 start_one_pdflush_thread(); //启动新的pdflush线程 143 spin_lock_irq(&pdflush_lock); //解锁pdflush_lock 144 } 145 } 146 } 147 148 my_work->fn = NULL; //加下盘回调指针置空 149 150 /* 151 * Thread destruction: For how long has the sleepiest 152 * thread slept? 153 */ 154 if (list_empty(&pdflush_list)) //如果pdflush_list链表为空,说明没有pdflush线程睡眠,继续循环 155 continue; 156 if (nr_pdflush_threads <= MIN_PDFLUSH_THREADS) //pdflush线程数小于最小pdflush线程数,继续循环 157 continue; 158 pdf = list_entry(pdflush_list.prev, struct pdflush_work, list); //获得链表中,前一个pdflush指针 159 if (time_after(jiffies, pdf->when_i_went_to_sleep + 1 * HZ)) { //该线程是否已经休眠了1hz以上的时间 160 /* Limit exit rate */ 161 pdf->when_i_went_to_sleep = jiffies; //将当前的时间更新为休眠时间 162 break; /* exeunt */ 163 } 164 } 165 nr_pdflush_threads--; //pdflush总数减一 166 spin_unlock_irq(&pdflush_lock); //解锁pdflush_lock 167 return 0; 168 } 169 170 /* 171 * Of course, my_work wants to be just a local in __pdflush(). It is 172 * separated out in this manner to hopefully prevent the compiler from 173 * performing unfortunate optimisations against the auto variables. Because 174 * these are visible to other tasks and CPUs. (No problem has actually 175 * been observed. This is just paranoia). 176 */ 177 static int pdflush(void *dummy) 178 { 179 struct pdflush_work my_work; 180 cpumask_var_t cpus_allowed; 181 182 /* 183 * Since the caller doesn't even check kthread_run() worked, let's not 184 * freak out too much if this fails. 185 */ 186 if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) { 187 printk(KERN_WARNING "pdflush failed to allocate cpumask\n"); 188 return 0; 189 } 190 191 /* 192 * pdflush can spend a lot of time doing encryption via dm-crypt. We 193 * don't want to do that at keventd's priority. 194 */ 195 set_user_nice(current, 0); 196 197 /* 198 * Some configs put our parent kthread in a limited cpuset, 199 * which kthread() overrides, forcing cpus_allowed == cpu_all_mask. 200 * Our needs are more modest - cut back to our cpusets cpus_allowed. 201 * This is needed as pdflush's are dynamically created and destroyed. 202 * The boottime pdflush's are easily placed w/o these 2 lines. 203 */ 204 cpuset_cpus_allowed(current, cpus_allowed); 205 set_cpus_allowed_ptr(current, cpus_allowed); 206 free_cpumask_var(cpus_allowed); 207 208 return __pdflush(&my_work); 209 } 210 211 /* 212 * Attempt to wake up a pdflush thread, and get it to do some work for you. 213 * Returns zero if it indeed managed to find a worker thread, and passed your 214 * payload to it. 215 */ 216 int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0) /* 用来尝试激活pdflush函数,用在writeback等需要回写脏页的函数中 */ 217 { 218 unsigned long flags; 219 int ret = 0; 220 221 BUG_ON(fn == NULL); /* Hard to diagnose if it's deferred */ 222 223 spin_lock_irqsave(&pdflush_lock, flags); 224 if (list_empty(&pdflush_list)) { //如果pdflush_list为空,说明pdflush内核线程已经够多了,不需要启动了 225 ret = -1; 226 } else { 227 struct pdflush_work *pdf; 228 229 pdf = list_entry(pdflush_list.next, struct pdflush_work, list); /* 从pdflush休眠链表中取出一个pdflush线程指针 */ 230 list_del_init(&pdf->list); 231 if (list_empty(&pdflush_list)) /* 如果这时候,pdflush休眠线程链表为空,则记录last_empty_jifs的时间 */ 232 last_empty_jifs = jiffies; 233 pdf->fn = fn; /* 为回写脏页的回调函数指针赋值 */ 234 pdf->arg0 = arg0; /* 赋回调函数的参数 */ 235 wake_up_process(pdf->who); /* 唤醒该pdflush内核线程 */ 236 } 237 spin_unlock_irqrestore(&pdflush_lock, flags); 238 239 return ret; 240 } 241
242 static void start_one_pdflush_thread(void) /* 用来启动一个新的pdflush线程 */ 243 { 244 struct task_struct *k; 245 246 k = kthread_run(pdflush, NULL, "pdflush"); /* 启动一个新的内核线程 */ 247 if (unlikely(IS_ERR(k))) { /* 启动失败的异常处理 */ 248 spin_lock_irq(&pdflush_lock); 249 nr_pdflush_threads--; 250 spin_unlock_irq(&pdflush_lock); 251 } 252 } 253 254 static int __init pdflush_init(void) /* 初始化pdflush模块 */ 255 { 256 int i; 257 258 /* 259 * Pre-set nr_pdflush_threads... If we fail to create, 260 * the count will be decremented. 261 */ 262 nr_pdflush_threads = MIN_PDFLUSH_THREADS; /* 模块启动时,按照最少线程数启动pdflush内核线程 */ 263 264 for (i = 0; i < MIN_PDFLUSH_THREADS; i++) /* 循环启动内核线程 */ 265 start_one_pdflush_thread(); 266 return 0; 267 } 268 269 module_init(pdflush_init); 270
可以参考 http://liurugongzi.blog.sohu.com/155184332.html