The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mm/pdflush.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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    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/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
   31 #define MAX_PDFLUSH_THREADS     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  */
   48 static LIST_HEAD(pdflush_list);
   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;
   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;
   98         INIT_LIST_HEAD(&my_work->list);
   99 
  100         spin_lock_irq(&pdflush_lock);
  101         nr_pdflush_threads++;
  102         for ( ; ; ) {
  103                 struct pdflush_work *pdf;
  104 
  105                 set_current_state(TASK_INTERRUPTIBLE);
  106                 list_move(&my_work->list, &pdflush_list);
  107                 my_work->when_i_went_to_sleep = jiffies;
  108                 spin_unlock_irq(&pdflush_lock);
  109                 schedule();
  110                 try_to_freeze();
  111                 spin_lock_irq(&pdflush_lock);
  112                 if (!list_empty(&my_work->list)) {
  113                         /*
  114                          * Someone woke us up, but without removing our control
  115                          * structure from the global list.  swsusp will do this
  116                          * in try_to_freeze()->refrigerator().  Handle it.
  117                          */
  118                         my_work->fn = NULL;
  119                         continue;
  120                 }
  121                 if (my_work->fn == NULL) {
  122                         printk("pdflush: bogus wakeup\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 (time_after(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 (time_after(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  */
  172 static int pdflush(void *dummy)
  173 {
  174         struct pdflush_work my_work;
  175         cpumask_t cpus_allowed;
  176 
  177         /*
  178          * pdflush can spend a lot of time doing encryption via dm-crypt.  We
  179          * don't want to do that at keventd's priority.
  180          */
  181         set_user_nice(current, 0);
  182 
  183         /*
  184          * Some configs put our parent kthread in a limited cpuset,
  185          * which kthread() overrides, forcing cpus_allowed == CPU_MASK_ALL.
  186          * Our needs are more modest - cut back to our cpusets cpus_allowed.
  187          * This is needed as pdflush's are dynamically created and destroyed.
  188          * The boottime pdflush's are easily placed w/o these 2 lines.
  189          */
  190         cpuset_cpus_allowed(current, &cpus_allowed);
  191         set_cpus_allowed_ptr(current, &cpus_allowed);
  192 
  193         return __pdflush(&my_work);
  194 }
  195 
  196 /*
  197  * Attempt to wake up a pdflush thread, and get it to do some work for you.
  198  * Returns zero if it indeed managed to find a worker thread, and passed your
  199  * payload to it.
  200  */
  201 int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
  202 {
  203         unsigned long flags;
  204         int ret = 0;
  205 
  206         BUG_ON(fn == NULL);     /* Hard to diagnose if it's deferred */
  207 
  208         spin_lock_irqsave(&pdflush_lock, flags);
  209         if (list_empty(&pdflush_list)) {
  210                 ret = -1;
  211         } else {
  212                 struct pdflush_work *pdf;
  213 
  214                 pdf = list_entry(pdflush_list.next, struct pdflush_work, list);
  215                 list_del_init(&pdf->list);
  216                 if (list_empty(&pdflush_list))
  217                         last_empty_jifs = jiffies;
  218                 pdf->fn = fn;
  219                 pdf->arg0 = arg0;
  220                 wake_up_process(pdf->who);
  221         }
  222         spin_unlock_irqrestore(&pdflush_lock, flags);
  223 
  224         return ret;
  225 }
  226 
  227 static void start_one_pdflush_thread(void)
  228 {
  229         kthread_run(pdflush, NULL, "pdflush");
  230 }
  231 
  232 static int __init pdflush_init(void)
  233 {
  234         int i;
  235 
  236         for (i = 0; i < MIN_PDFLUSH_THREADS; i++)
  237                 start_one_pdflush_thread();
  238         return 0;
  239 }
  240 
  241 module_init(pdflush_init);

Cache object: d89e324809c0747e3b6dcaed9ef800b0


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.