nfsd: clean up readdirplus encoding
[safe/jmp/linux-2.6] / kernel / slow-work.c
index 454abb2..09d7519 100644 (file)
@@ -7,6 +7,8 @@
  * modify it under the terms of the GNU General Public Licence
  * as published by the Free Software Foundation; either version
  * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/slow-work.txt
  */
 
 #include <linux/module.h>
@@ -14,7 +16,6 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 #include <linux/wait.h>
-#include <asm/system.h>
 
 #define SLOW_WORK_CULL_TIMEOUT (5 * HZ)        /* cull threads 5s after running out of
                                         * things to do */
 static void slow_work_cull_timeout(unsigned long);
 static void slow_work_oom_timeout(unsigned long);
 
+#ifdef CONFIG_SYSCTL
+static int slow_work_min_threads_sysctl(struct ctl_table *, int, struct file *,
+                                       void __user *, size_t *, loff_t *);
+
+static int slow_work_max_threads_sysctl(struct ctl_table *, int , struct file *,
+                                       void __user *, size_t *, loff_t *);
+#endif
+
 /*
  * The pool of threads has at least min threads in it as long as someone is
  * using the facility, and may have as many as max.
@@ -34,6 +43,51 @@ static unsigned slow_work_min_threads = 2;
 static unsigned slow_work_max_threads = 4;
 static unsigned vslow_work_proportion = 50; /* % of threads that may process
                                             * very slow work */
+
+#ifdef CONFIG_SYSCTL
+static const int slow_work_min_min_threads = 2;
+static int slow_work_max_max_threads = 255;
+static const int slow_work_min_vslow = 1;
+static const int slow_work_max_vslow = 99;
+
+ctl_table slow_work_sysctls[] = {
+       {
+               .ctl_name       = CTL_UNNUMBERED,
+               .procname       = "min-threads",
+               .data           = &slow_work_min_threads,
+               .maxlen         = sizeof(unsigned),
+               .mode           = 0644,
+               .proc_handler   = slow_work_min_threads_sysctl,
+               .extra1         = (void *) &slow_work_min_min_threads,
+               .extra2         = &slow_work_max_threads,
+       },
+       {
+               .ctl_name       = CTL_UNNUMBERED,
+               .procname       = "max-threads",
+               .data           = &slow_work_max_threads,
+               .maxlen         = sizeof(unsigned),
+               .mode           = 0644,
+               .proc_handler   = slow_work_max_threads_sysctl,
+               .extra1         = &slow_work_min_threads,
+               .extra2         = (void *) &slow_work_max_max_threads,
+       },
+       {
+               .ctl_name       = CTL_UNNUMBERED,
+               .procname       = "vslow-percentage",
+               .data           = &vslow_work_proportion,
+               .maxlen         = sizeof(unsigned),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec_minmax,
+               .extra1         = (void *) &slow_work_min_vslow,
+               .extra2         = (void *) &slow_work_max_vslow,
+       },
+       { .ctl_name = 0 }
+};
+#endif
+
+/*
+ * The active state of the thread pool
+ */
 static atomic_t slow_work_thread_count;
 static atomic_t vslow_work_executing_count;
 
@@ -265,6 +319,15 @@ cant_get_ref:
 EXPORT_SYMBOL(slow_work_enqueue);
 
 /*
+ * Schedule a cull of the thread pool at some time in the near future
+ */
+static void slow_work_schedule_cull(void)
+{
+       mod_timer(&slow_work_cull_timer,
+                 round_jiffies(jiffies + SLOW_WORK_CULL_TIMEOUT));
+}
+
+/*
  * Worker thread culling algorithm
  */
 static bool slow_work_cull_thread(void)
@@ -281,8 +344,7 @@ static bool slow_work_cull_thread(void)
                    list_empty(&vslow_work_queue) &&
                    atomic_read(&slow_work_thread_count) >
                    slow_work_min_threads) {
-                       mod_timer(&slow_work_cull_timer,
-                                 jiffies + SLOW_WORK_CULL_TIMEOUT);
+                       slow_work_schedule_cull();
                        do_cull = true;
                }
        }
@@ -318,8 +380,8 @@ static int slow_work_thread(void *_data)
                vsmax *= atomic_read(&slow_work_thread_count);
                vsmax /= 100;
 
-               prepare_to_wait(&slow_work_thread_wq, &wait,
-                               TASK_INTERRUPTIBLE);
+               prepare_to_wait_exclusive(&slow_work_thread_wq, &wait,
+                                         TASK_INTERRUPTIBLE);
                if (!freezing(current) &&
                    !slow_work_threads_should_exit &&
                    !slow_work_available(vsmax) &&
@@ -339,8 +401,7 @@ static int slow_work_thread(void *_data)
                            list_empty(&vslow_work_queue) &&
                            atomic_read(&slow_work_thread_count) >
                            slow_work_min_threads)
-                               mod_timer(&slow_work_cull_timer,
-                                         jiffies + SLOW_WORK_CULL_TIMEOUT);
+                               slow_work_schedule_cull();
                        continue;
                }
 
@@ -404,7 +465,7 @@ static void slow_work_new_thread_execute(struct slow_work *work)
                if (atomic_dec_and_test(&slow_work_thread_count))
                        BUG(); /* we're running on a slow work thread... */
                mod_timer(&slow_work_oom_timer,
-                         jiffies + SLOW_WORK_OOM_TIMEOUT);
+                         round_jiffies(jiffies + SLOW_WORK_OOM_TIMEOUT));
        } else {
                /* ratelimit the starting of new threads */
                mod_timer(&slow_work_oom_timer, jiffies + 1);
@@ -427,6 +488,62 @@ static void slow_work_oom_timeout(unsigned long data)
        slow_work_may_not_start_new_thread = false;
 }
 
+#ifdef CONFIG_SYSCTL
+/*
+ * Handle adjustment of the minimum number of threads
+ */
+static int slow_work_min_threads_sysctl(struct ctl_table *table, int write,
+                                       struct file *filp, void __user *buffer,
+                                       size_t *lenp, loff_t *ppos)
+{
+       int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+       int n;
+
+       if (ret == 0) {
+               mutex_lock(&slow_work_user_lock);
+               if (slow_work_user_count > 0) {
+                       /* see if we need to start or stop threads */
+                       n = atomic_read(&slow_work_thread_count) -
+                               slow_work_min_threads;
+
+                       if (n < 0 && !slow_work_may_not_start_new_thread)
+                               slow_work_enqueue(&slow_work_new_thread);
+                       else if (n > 0)
+                               slow_work_schedule_cull();
+               }
+               mutex_unlock(&slow_work_user_lock);
+       }
+
+       return ret;
+}
+
+/*
+ * Handle adjustment of the maximum number of threads
+ */
+static int slow_work_max_threads_sysctl(struct ctl_table *table, int write,
+                                       struct file *filp, void __user *buffer,
+                                       size_t *lenp, loff_t *ppos)
+{
+       int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+       int n;
+
+       if (ret == 0) {
+               mutex_lock(&slow_work_user_lock);
+               if (slow_work_user_count > 0) {
+                       /* see if we need to stop threads */
+                       n = slow_work_max_threads -
+                               atomic_read(&slow_work_thread_count);
+
+                       if (n < 0)
+                               slow_work_schedule_cull();
+               }
+               mutex_unlock(&slow_work_user_lock);
+       }
+
+       return ret;
+}
+#endif /* CONFIG_SYSCTL */
+
 /**
  * slow_work_register_user - Register a user of the facility
  *
@@ -497,14 +614,14 @@ void slow_work_unregister_user(void)
        if (slow_work_user_count == 0) {
                printk(KERN_NOTICE "Slow work thread pool: Shutting down\n");
                slow_work_threads_should_exit = true;
+               del_timer_sync(&slow_work_cull_timer);
+               del_timer_sync(&slow_work_oom_timer);
                wake_up_all(&slow_work_thread_wq);
                wait_for_completion(&slow_work_last_thread_exited);
                printk(KERN_NOTICE "Slow work thread pool:"
                       " Shut down complete\n");
        }
 
-       del_timer_sync(&slow_work_cull_timer);
-
        mutex_unlock(&slow_work_user_lock);
 }
 EXPORT_SYMBOL(slow_work_unregister_user);
@@ -516,8 +633,12 @@ static int __init init_slow_work(void)
 {
        unsigned nr_cpus = num_possible_cpus();
 
-       if (nr_cpus > slow_work_max_threads)
+       if (slow_work_max_threads < nr_cpus)
                slow_work_max_threads = nr_cpus;
+#ifdef CONFIG_SYSCTL
+       if (slow_work_max_max_threads < nr_cpus * 2)
+               slow_work_max_max_threads = nr_cpus * 2;
+#endif
        return 0;
 }