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