KERNEL - Implement a poor man's ioscheduler using sys/iosched.h
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 11 Sep 2009 02:12:08 +0000 (19:12 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 11 Sep 2009 02:12:08 +0000 (19:12 -0700)
* Move struct iosched_data from the LWP to the TD.

* Expand bd_wait()'s wakeup array from 128 entries to 16384 entries
  to cover a larger portion of the buffer cache's dirty space.

* bwillwrite() now adds to iosched_data->iowbytes and the ticks
  global is used to decay it.

  Total iowbytes is kept in per-cpu structures.  Each cpu does not
  necessarily match the threads assigned to it, but in aggregate
  the sum of iowbytes for all cpus will match the sum of iowbytes
  for all threads in the system.

* bwillwrite() now calculates a relative I/O write load for each thread
  in the system and passes a fractional calculation to bd_wait().

  This causes processes with low I/O loads to have a higher priority
  (as in, NOT stall in bd_wait() anywhere near as long), and processes
  with high I/O loads to have a lower priority (stall longer in bd_wait()),
  when the buffer cache is saturated with dirty data.

sys/kern/kern_exit.c
sys/kern/kern_iosched.c
sys/kern/lwkt_thread.c
sys/kern/vfs_bio.c
sys/sys/iosched.h
sys/sys/proc.h
sys/sys/thread.h

index 8d75ace..65a0981 100644 (file)
@@ -590,6 +590,7 @@ lwp_exit(int masterexit)
        } else {
                --p->p_nthreads;
        }
+       biosched_done(curthread);
        cpu_lwp_exit();
 }
 
index 7b36c4e..310130e 100644 (file)
@@ -43,7 +43,9 @@
 #include <machine/cpu.h>
 #include <sys/spinlock.h>
 #include <sys/iosched.h>
+#include <sys/sysctl.h>
 #include <sys/buf.h>
+#include <sys/limits.h>
 
 #include <sys/thread2.h>
 #include <sys/spinlock2.h>
 #include <vm/vm_kern.h>
 #include <vm/vm_extern.h>
 
+SYSCTL_NODE(, OID_AUTO, iosched, CTLFLAG_RW, 0, "I/O Scheduler");
+
+static int iosched_debug = 0;
+SYSCTL_INT(_iosched, OID_AUTO, debug, CTLFLAG_RW, &iosched_debug, 0, "");
+
+static struct iosched_data     ioscpu[SMP_MAXCPU];
+
+static int
+badjiosched(thread_t td, size_t bytes)
+{
+       globaldata_t gd = mycpu;
+       size_t iostotal;
+       int factor;
+       int i;
+       int delta;
+
+       iostotal = 0;
+       for (i = 0; i < ncpus; ++i)
+               iostotal += ioscpu[i].iowbytes;
+       if (SIZE_T_MAX / SMP_MAXCPU - td->td_iosdata.iowbytes < bytes)
+               bytes = SIZE_T_MAX / SMP_MAXCPU - td->td_iosdata.iowbytes;
+       td->td_iosdata.iowbytes += bytes;
+       ioscpu[gd->gd_cpuid].iowbytes += bytes;
+       iostotal += bytes;
+       delta = ticks - td->td_iosdata.lastticks;
+       if (delta) {
+               td->td_iosdata.lastticks = ticks;
+               if (delta < 0 || delta > hz * 10)
+                       delta = hz * 10;
+               bytes = (int64_t)td->td_iosdata.iowbytes * delta / (hz * 10);
+               td->td_iosdata.iowbytes -= bytes;
+               ioscpu[gd->gd_cpuid].iowbytes -= bytes;
+               iostotal -= bytes;
+       }
+       if (iostotal > 0)
+               factor = td->td_iosdata.iowbytes * 100 / iostotal;
+       else
+               factor = 50;
+       if (delta && (iosched_debug & 1)) {
+               kprintf("proc %12s (%-5d) factor %3d (%zd/%zd)\n",
+                       td->td_comm,
+                       (td->td_lwp ? (int)td->td_lwp->lwp_proc->p_pid : -1),
+                       factor, td->td_iosdata.iowbytes, iostotal);
+       }
+       return (factor);
+}
+
+void
+biosched_done(thread_t td)
+{
+       globaldata_t gd = mycpu;
+       size_t bytes;
+
+       if ((bytes = td->td_iosdata.iowbytes) != 0) {
+               td->td_iosdata.iowbytes = 0;
+               ioscpu[gd->gd_cpuid].iowbytes -= bytes;
+       }
+}
+
 /*
  * Caller intends to write (bytes)
  */
@@ -60,10 +121,14 @@ void
 bwillwrite(int bytes)
 {
        int count;
+       int factor;
 
        count = bd_heatup();
-       if (count > 0)
-               bd_wait(count / 2);
+       if (count > 0) {
+               factor = badjiosched(curthread, (size_t)bytes);
+               count = hidirtybufspace * factor / 100;
+               bd_wait(count);
+       }
 }
 
 /*
@@ -81,9 +146,13 @@ void
 bwillinode(int n)
 {
        int count;
+       int factor;
 
        count = bd_heatup();
-       if (count > 0)
-               bd_wait(1);
+       if (count > 0) {
+               factor = badjiosched(curthread, PAGE_SIZE);
+               count = count * factor / 100;
+               bd_wait(count);
+       }
 }
 
index 9c3be36..285750f 100644 (file)
@@ -1476,6 +1476,7 @@ lwkt_exit(void)
      */
     if (td->td_flags & TDF_TSLEEPQ)
        tsleep_remove(td);
+    biosched_done(td);
     lwkt_deschedule_self(td);
     lwkt_remove_tdallq(td);
     if (td->td_flags & TDF_ALLOCATED_THREAD)
index 3c9d80c..6d75070 100644 (file)
@@ -81,7 +81,7 @@ enum bufq_type {
 
 typedef enum bufq_type bufq_type_t;
 
-#define BD_WAKE_SIZE   128
+#define BD_WAKE_SIZE   16384
 #define BD_WAKE_MASK   (BD_WAKE_SIZE - 1)
 
 TAILQ_HEAD(bqueues, buf) bufqueues[BUFFER_QUEUES];
@@ -393,6 +393,9 @@ bd_speedup(void)
  *     Get the buf_daemon heated up when the number of running and dirty
  *     buffers exceeds the mid-point.
  *
+ *     Return the total number of dirty bytes past the second mid point
+ *     as a measure of how much excess dirty data there is in the system.
+ *
  * MPSAFE
  */
 int
index 3e6677e..506fad8 100644 (file)
 #endif
 
 struct iosched_data {
-    int                iorbytes;
-    int                iowbytes;
+    size_t     iorbytes;
+    size_t     iowbytes;
+    int                lastticks;      /* decay last recorded */
 };
 
 #endif /* _KERNEL || _KERNEL_STRUCTURES */
 
 #ifdef _KERNEL
 
+struct thread;
 void    bwillwrite(int bytes);
 void    bwillread(int bytes);
 void    bwillinode(int count);
+void   biosched_done(struct thread *td);
 
 #endif
 
index 86745f6..23fe8a8 100644 (file)
@@ -201,7 +201,6 @@ struct lwp {
        struct rusage   lwp_ru;         /* stats for this lwp */
 
        union usched_data lwp_usdata;   /* User scheduler specific */
-       struct iosched_data lwp_iosdata; /* Dynamic I/O scheduling data */
 
 #define lwp_startcopy  lwp_cpumask
        cpumask_t       lwp_cpumask;
index bce1908..7cc006f 100644 (file)
@@ -31,6 +31,9 @@
 #ifndef _SYS_SPINLOCK_H_
 #include <sys/spinlock.h>
 #endif
+#ifndef _SYS_IOSCHED_H_
+#include <sys/iosched.h>
+#endif
 #ifndef _MACHINE_THREAD_H_
 #include <machine/thread.h>
 #endif
@@ -246,6 +249,7 @@ struct thread {
     int                td_mpcount_unused;      /* filler so size matches */
     int                td_cscount_unused;
 #endif
+    struct iosched_data td_iosdata;    /* Dynamic I/O scheduling data */
     struct timeval td_start;   /* start time for a thread/process */
     char       td_comm[MAXCOMLEN+1]; /* typ 16+1 bytes */
     struct thread *td_preempted; /* we preempted this thread */