RHEL4/mm/pdflush.c
<<
>>
Prefs
   1/*
   2 * mm/pdflush.c - worker threads for writing back filesystem data
   3 *
   4 * Copyright (C) 2002, Linus Torvalds.
   5 *
   6 * 09Apr2002    akpm@zip.com.au
   7 *              Initial version
   8 * 29Feb2004    kaos@sgi.com
   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/suspend.h>
  21#include <linux/fs.h>           // Needed by writeback.h
  22#include <linux/writeback.h>    // Prototypes pdflush_operation()
  23#include <linux/kthread.h>
  24
  25
  26/*
  27 * Minimum and maximum number of pdflush instances
  28 */
  29#define MIN_PDFLUSH_THREADS     2
  30#define MAX_PDFLUSH_THREADS     8
  31
  32static void start_one_pdflush_thread(void);
  33
  34
  35/*
  36 * The pdflush threads are worker threads for writing back dirty data.
  37 * Ideally, we'd like one thread per active disk spindle.  But the disk
  38 * topology is very hard to divine at this level.   Instead, we take
  39 * care in various places to prevent more than one pdflush thread from
  40 * performing writeback against a single filesystem.  pdflush threads
  41 * have the PF_FLUSHER flag set in current->flags to aid in this.
  42 */
  43
  44/*
  45 * All the pdflush threads.  Protected by pdflush_lock
  46 */
  47static LIST_HEAD(pdflush_list);
  48static spinlock_t pdflush_lock = SPIN_LOCK_UNLOCKED;
  49
  50/*
  51 * The count of currently-running pdflush threads.  Protected
  52 * by pdflush_lock.
  53 *
  54 * Readable by sysctl, but not writable.  Published to userspace at
  55 * /proc/sys/vm/nr_pdflush_threads.
  56 */
  57int nr_pdflush_threads = 0;
  58
  59/*
  60 * The time at which the pdflush thread pool last went empty
  61 */
  62static unsigned long last_empty_jifs;
  63
  64/*
  65 * The pdflush thread.
  66 *
  67 * Thread pool management algorithm:
  68 * 
  69 * - The minimum and maximum number of pdflush instances are bound
  70 *   by MIN_PDFLUSH_THREADS and MAX_PDFLUSH_THREADS.
  71 * 
  72 * - If there have been no idle pdflush instances for 1 second, create
  73 *   a new one.
  74 * 
  75 * - If the least-recently-went-to-sleep pdflush thread has been asleep
  76 *   for more than one second, terminate a thread.
  77 */
  78
  79/*
  80 * A structure for passing work to a pdflush thread.  Also for passing
  81 * state information between pdflush threads.  Protected by pdflush_lock.
  82 */
  83struct pdflush_work {
  84        struct task_struct *who;        /* The thread */
  85        void (*fn)(unsigned long);      /* A callback function */
  86        unsigned long arg0;             /* An argument to the callback */
  87        struct list_head list;          /* On pdflush_list, when idle */
  88        unsigned long when_i_went_to_sleep;
  89};
  90
  91static int __pdflush(struct pdflush_work *my_work)
  92{
  93        current->flags |= PF_FLUSHER;
  94        my_work->fn = NULL;
  95        my_work->who = current;
  96        INIT_LIST_HEAD(&my_work->list);
  97
  98        spin_lock_irq(&pdflush_lock);
  99        nr_pdflush_threads++;
 100        for ( ; ; ) {
 101                struct pdflush_work *pdf;
 102
 103                set_current_state(TASK_INTERRUPTIBLE);
 104                list_move(&my_work->list, &pdflush_list);
 105                my_work->when_i_went_to_sleep = jiffies;
 106                spin_unlock_irq(&pdflush_lock);
 107
 108                schedule();
 109                if (current->flags & PF_FREEZE) {
 110                        refrigerator(PF_FREEZE);
 111                        spin_lock_irq(&pdflush_lock);
 112                        continue;
 113                }
 114
 115                spin_lock_irq(&pdflush_lock);
 116                if (!list_empty(&my_work->list)) {
 117                        printk("pdflush: bogus wakeup!\n");
 118                        my_work->fn = NULL;
 119                        continue;
 120                }
 121                if (my_work->fn == NULL) {
 122                        printk("pdflush: NULL work function\n");
 123                        continue;
 124                }
 125                spin_unlock_irq(&pdflush_lock);
 126
 127                (*my_work->fn)(my_work->arg0);
 128
 129                /*
 130                 * Thread creation: For how long have there been zero
 131                 * available threads?
 132                 */
 133                if (jiffies - last_empty_jifs > 1 * HZ) {
 134                        /* unlocked list_empty() test is OK here */
 135                        if (list_empty(&pdflush_list)) {
 136                                /* unlocked test is OK here */
 137                                if (nr_pdflush_threads < MAX_PDFLUSH_THREADS)
 138                                        start_one_pdflush_thread();
 139                        }
 140                }
 141
 142                spin_lock_irq(&pdflush_lock);
 143                my_work->fn = NULL;
 144
 145                /*
 146                 * Thread destruction: For how long has the sleepiest
 147                 * thread slept?
 148                 */
 149                if (list_empty(&pdflush_list))
 150                        continue;
 151                if (nr_pdflush_threads <= MIN_PDFLUSH_THREADS)
 152                        continue;
 153                pdf = list_entry(pdflush_list.prev, struct pdflush_work, list);
 154                if (jiffies - pdf->when_i_went_to_sleep > 1 * HZ) {
 155                        /* Limit exit rate */
 156                        pdf->when_i_went_to_sleep = jiffies;
 157                        break;                                  /* exeunt */
 158                }
 159        }
 160        nr_pdflush_threads--;
 161        spin_unlock_irq(&pdflush_lock);
 162        return 0;
 163}
 164
 165/*
 166 * Of course, my_work wants to be just a local in __pdflush().  It is
 167 * separated out in this manner to hopefully prevent the compiler from
 168 * performing unfortunate optimisations against the auto variables.  Because
 169 * these are visible to other tasks and CPUs.  (No problem has actually
 170 * been observed.  This is just paranoia).
 171 */
 172static int pdflush(void *dummy)
 173{
 174        struct pdflush_work my_work;
 175
 176        /*
 177         * pdflush can spend a lot of time doing encryption via dm-crypt.  We
 178         * don't want to do that at keventd's priority.
 179         */
 180        set_user_nice(current, 0);
 181        return __pdflush(&my_work);
 182}
 183
 184/*
 185 * Attempt to wake up a pdflush thread, and get it to do some work for you.
 186 * Returns zero if it indeed managed to find a worker thread, and passed your
 187 * payload to it.
 188 */
 189int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
 190{
 191        unsigned long flags;
 192        int ret = 0;
 193
 194        if (fn == NULL)
 195                BUG();          /* Hard to diagnose if it's deferred */
 196
 197        spin_lock_irqsave(&pdflush_lock, flags);
 198        if (list_empty(&pdflush_list)) {
 199                spin_unlock_irqrestore(&pdflush_lock, flags);
 200                ret = -1;
 201        } else {
 202                struct pdflush_work *pdf;
 203
 204                pdf = list_entry(pdflush_list.next, struct pdflush_work, list);
 205                list_del_init(&pdf->list);
 206                if (list_empty(&pdflush_list))
 207                        last_empty_jifs = jiffies;
 208                pdf->fn = fn;
 209                pdf->arg0 = arg0;
 210                wake_up_process(pdf->who);
 211                spin_unlock_irqrestore(&pdflush_lock, flags);
 212        }
 213        return ret;
 214}
 215
 216static void start_one_pdflush_thread(void)
 217{
 218        kthread_run(pdflush, NULL, "pdflush");
 219}
 220
 221static int __init pdflush_init(void)
 222{
 223        int i;
 224
 225        for (i = 0; i < MIN_PDFLUSH_THREADS; i++)
 226                start_one_pdflush_thread();
 227        return 0;
 228}
 229
 230module_init(pdflush_init);
 231