kernel -- Per-mount syncer thread infrastructure.
[dragonfly.git] / sys / kern / vfs_sync.c
index 3cae967..4591043 100644 (file)
@@ -37,7 +37,6 @@
  *
  *     @(#)vfs_subr.c  8.31 (Berkeley) 5/26/95
  * $FreeBSD: src/sys/kern/vfs_subr.c,v 1.249.2.30 2003/04/04 20:35:57 tegge Exp $
- * $DragonFly: src/sys/kern/vfs_sync.c,v 1.18 2008/05/18 05:54:25 dillon Exp $
  */
 
 /*
@@ -82,7 +81,6 @@
 
 #include <sys/buf2.h>
 #include <sys/thread2.h>
-#include <sys/mplock2.h>
 
 /*
  * The workitem queue.
@@ -106,11 +104,38 @@ static int stat_rush_requests;    /* number of times I/O speeded up */
 SYSCTL_INT(_debug, OID_AUTO, rush_requests, CTLFLAG_RW,
                &stat_rush_requests, 0, "");
 
-static int syncer_delayno = 0;
-static long syncer_mask; 
-static struct lwkt_token syncer_token;
 LIST_HEAD(synclist, vnode);
-static struct synclist *syncer_workitem_pending;
+
+#define        SC_FLAG_EXIT            (0x1)           /* request syncer exit */
+#define        SC_FLAG_DONE            (0x2)           /* syncer confirm exit */
+#define        SC_FLAG_BIOOPS_ALL      (0x4)           /* do bufops_sync(NULL) */
+
+struct syncer_ctx {
+       struct mount            *sc_mp;
+       struct lwkt_token       sc_token;
+       struct thread           *sc_thread;
+       int                     sc_flags;
+
+       struct synclist         *syncer_workitem_pending;
+       long                    syncer_mask;
+       int                     syncer_delayno;
+};
+
+static struct syncer_ctx syncer_ctx0;
+
+static void syncer_thread(void *);
+
+static void
+syncer_ctx_init(struct syncer_ctx *ctx, struct mount *mp)
+{
+       ctx->sc_mp = mp; 
+       lwkt_token_init(&ctx->sc_token, "syncer");
+       ctx->sc_flags = 0;
+
+       ctx->syncer_workitem_pending = hashinit(syncer_maxdelay, M_DEVBUF,
+                                               &ctx->syncer_mask);
+       ctx->syncer_delayno = 0;
+}
 
 /*
  * Called from vfsinit()
@@ -118,10 +143,27 @@ static struct synclist *syncer_workitem_pending;
 void
 vfs_sync_init(void)
 {
-       syncer_workitem_pending = hashinit(syncer_maxdelay, M_DEVBUF,
-                                           &syncer_mask);
-       syncer_maxdelay = syncer_mask + 1;
-       lwkt_token_init(&syncer_token, 1, "syncer");
+       syncer_ctx_init(&syncer_ctx0, NULL);
+       syncer_maxdelay = syncer_ctx0.syncer_mask + 1;
+       syncer_ctx0.sc_flags |= SC_FLAG_BIOOPS_ALL;
+       
+       /* Support schedcpu wakeup of syncer0 */
+       lbolt_syncer = &syncer_ctx0;
+}
+
+static struct syncer_ctx *
+vn_get_syncer(struct vnode *vp) {
+       struct mount *mp;
+       struct syncer_ctx *ctx;
+
+       ctx = NULL;
+       mp = vp->v_mount;
+       if (mp)
+               ctx = mp->mnt_syncer_ctx;
+       if (ctx == NULL)
+               ctx = &syncer_ctx0;
+
+       return (ctx);
 }
 
 /*
@@ -162,20 +204,23 @@ vfs_sync_init(void)
 void
 vn_syncer_add(struct vnode *vp, int delay)
 {
+       struct syncer_ctx *ctx;
        int slot;
 
-       lwkt_gettoken(&syncer_token);
+       ctx = vn_get_syncer(vp);
+
+       lwkt_gettoken(&ctx->sc_token);
 
        if (vp->v_flag & VONWORKLST)
                LIST_REMOVE(vp, v_synclist);
        if (delay > syncer_maxdelay - 2)
                delay = syncer_maxdelay - 2;
-       slot = (syncer_delayno + delay) & syncer_mask;
+       slot = (ctx->syncer_delayno + delay) & ctx->syncer_mask;
 
-       LIST_INSERT_HEAD(&syncer_workitem_pending[slot], vp, v_synclist);
+       LIST_INSERT_HEAD(&ctx->syncer_workitem_pending[slot], vp, v_synclist);
        vsetflags(vp, VONWORKLST);
 
-       lwkt_reltoken(&syncer_token);
+       lwkt_reltoken(&ctx->sc_token);
 }
 
 /*
@@ -187,54 +232,110 @@ vn_syncer_add(struct vnode *vp, int delay)
 void
 vn_syncer_remove(struct vnode *vp)
 {
-       lwkt_gettoken(&syncer_token);
+       struct syncer_ctx *ctx;
+
+       ctx = vn_get_syncer(vp);
+
+       lwkt_gettoken(&ctx->sc_token);
 
        if ((vp->v_flag & VONWORKLST) && RB_EMPTY(&vp->v_rbdirty_tree)) {
                vclrflags(vp, VONWORKLST);
                LIST_REMOVE(vp, v_synclist);
        }
 
-       lwkt_reltoken(&syncer_token);
+       lwkt_reltoken(&ctx->sc_token);
+}
+
+/*
+ * Create per-filesystem syncer process
+ */
+void
+vn_syncer_thr_create(struct mount *mp)
+{
+       struct syncer_ctx *ctx;
+       static int syncalloc = 0;
+       int rc;
+
+       ctx = kmalloc(sizeof(struct syncer_ctx), M_TEMP, M_WAITOK);
+
+       syncer_ctx_init(ctx, mp);
+       mp->mnt_syncer_ctx = ctx;
+
+       rc = kthread_create(syncer_thread, ctx, &ctx->sc_thread, 
+                           "syncer%d", ++syncalloc);
+}
+
+/*
+ * Stop per-filesystem syncer process
+ */
+void
+vn_syncer_thr_stop(struct mount *mp)
+{
+       struct syncer_ctx *ctx;
+
+       ctx = mp->mnt_syncer_ctx;
+
+       lwkt_gettoken(&ctx->sc_token);
+
+       /* Signal the syncer process to exit */
+       ctx->sc_flags |= SC_FLAG_EXIT;
+       wakeup(ctx);
+       
+       /* Wait till syncer process exits */
+       while ((ctx->sc_flags & SC_FLAG_DONE) == 0) 
+               tsleep(&ctx->sc_flags, 0, "syncexit", hz);
+
+       mp->mnt_syncer_ctx = NULL;
+       lwkt_reltoken(&ctx->sc_token);
+       
+       kfree(ctx->syncer_workitem_pending, M_DEVBUF);
+       kfree(ctx, M_TEMP);
 }
 
 struct  thread *updatethread;
 
 /*
  * System filesystem synchronizer daemon.
- *
- * NOTE: Started MPSAFE but is not yet mpsafe.
  */
 static void
-syncer_thread(void)
+syncer_thread(void *_ctx)
 {
        struct thread *td = curthread;
+       struct syncer_ctx *ctx = _ctx;
        struct synclist *slp;
        struct vnode *vp;
        long starttime;
+       int *sc_flagsp;
+       int sc_flags;
+       int vnodes_synced = 0;
 
-       EVENTHANDLER_REGISTER(shutdown_pre_sync, shutdown_kproc, td,
-                             SHUTDOWN_PRI_LAST);
-       get_mplock();
-
+       /*
+        * syncer0 runs till system shutdown; per-filesystem syncers are
+        * terminated on filesystem unmount
+        */
+       if (ctx == &syncer_ctx0) 
+               EVENTHANDLER_REGISTER(shutdown_pre_sync, shutdown_kproc, td,
+                                     SHUTDOWN_PRI_LAST);
        for (;;) {
                kproc_suspend_loop();
 
                starttime = time_second;
-               lwkt_gettoken(&syncer_token);
+               lwkt_gettoken(&ctx->sc_token);
 
                /*
                 * Push files whose dirty time has expired.  Be careful
                 * of interrupt race on slp queue.
                 */
-               slp = &syncer_workitem_pending[syncer_delayno];
-               syncer_delayno += 1;
-               if (syncer_delayno == syncer_maxdelay)
-                       syncer_delayno = 0;
+               slp = &ctx->syncer_workitem_pending[ctx->syncer_delayno];
+               ctx->syncer_delayno += 1;
+               if (ctx->syncer_delayno == syncer_maxdelay)
+                       ctx->syncer_delayno = 0;
 
                while ((vp = LIST_FIRST(slp)) != NULL) {
                        if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT) == 0) {
                                VOP_FSYNC(vp, MNT_LAZY, 0);
                                vput(vp);
+                               vnodes_synced++;
                        }
 
                        /*
@@ -261,12 +362,20 @@ syncer_thread(void)
                        if (LIST_FIRST(slp) == vp)
                                vn_syncer_add(vp, syncdelay);
                }
-               lwkt_reltoken(&syncer_token);
+
+               sc_flags = ctx->sc_flags;
+
+               /* Exit on unmount */
+               if (sc_flags & SC_FLAG_EXIT)
+                       break;
+
+               lwkt_reltoken(&ctx->sc_token);
 
                /*
                 * Do sync processing for each mount.
                 */
-               bio_ops_sync(NULL);
+               if (ctx->sc_mp || sc_flags & SC_FLAG_BIOOPS_ALL)
+                       bio_ops_sync(ctx->sc_mp);
 
                /*
                 * The variable rushjob allows the kernel to speed up the
@@ -278,8 +387,8 @@ syncer_thread(void)
                 * ahead of the disk that the kernel memory pool is being
                 * threatened with exhaustion.
                 */
-               if (rushjob > 0) {
-                       rushjob -= 1;
+               if (ctx == &syncer_ctx0 && rushjob > 0) {
+                       atomic_subtract_int(&rushjob, 1);
                        continue;
                }
                /*
@@ -291,14 +400,28 @@ syncer_thread(void)
                 * filesystem activity.
                 */
                if (time_second == starttime)
-                       tsleep(&lbolt_syncer, 0, "syncer", 0);
+                       tsleep(ctx, 0, "syncer", hz);
        }
-       rel_mplock();
+
+       /*
+        * Unmount/exit path for per-filesystem syncers; sc_token held
+        */
+       ctx->sc_flags |= SC_FLAG_DONE;
+       sc_flagsp = &ctx->sc_flags;
+       lwkt_reltoken(&ctx->sc_token);
+       wakeup(sc_flagsp);
+
+       kthread_exit();
+}
+
+static void
+syncer_thread_start(void) {
+       syncer_thread(&syncer_ctx0);
 }
 
 static struct kproc_desc up_kp = {
-       "syncer",
-       syncer_thread,
+       "syncer0",
+       syncer_thread_start,
        &updatethread
 };
 SYSINIT(syncer, SI_SUB_KTHREAD_UPDATE, SI_ORDER_FIRST, kproc_start, &up_kp)
@@ -307,8 +430,6 @@ SYSINIT(syncer, SI_SUB_KTHREAD_UPDATE, SI_ORDER_FIRST, kproc_start, &up_kp)
  * Request the syncer daemon to speed up its work.
  * We never push it to speed up more than half of its
  * normal turn time, otherwise it could take over the cpu.
- *
- * YYY wchan field protected by the BGL.
  */
 int
 speedup_syncer(void)
@@ -317,9 +438,9 @@ speedup_syncer(void)
         * Don't bother protecting the test.  unsleep_and_wakeup_thread()
         * will only do something real if the thread is in the right state.
         */
-       wakeup(&lbolt_syncer);
+       wakeup(lbolt_syncer);
        if (rushjob < syncdelay / 2) {
-               rushjob += 1;
+               atomic_add_int(&rushjob, 1);
                stat_rush_requests += 1;
                return (1);
        }
@@ -418,7 +539,7 @@ sync_fsync(struct vop_fsync_args *ap)
        /*
         * We only need to do something if this is a lazy evaluation.
         */
-       if (ap->a_waitfor != MNT_LAZY)
+       if ((ap->a_waitfor & MNT_LAZY) == 0)
                return (0);
 
        /*
@@ -441,7 +562,7 @@ sync_fsync(struct vop_fsync_args *ap)
                asyncflag = mp->mnt_flag & MNT_ASYNC;
                mp->mnt_flag &= ~MNT_ASYNC;     /* ZZZ hack */
                vfs_msync(mp, MNT_NOWAIT);
-               VFS_SYNC(mp, MNT_LAZY);
+               VFS_SYNC(mp, MNT_NOWAIT | MNT_LAZY);
                if (asyncflag)
                        mp->mnt_flag |= MNT_ASYNC;
        }
@@ -475,14 +596,17 @@ static int
 sync_reclaim(struct vop_reclaim_args *ap)
 {
        struct vnode *vp = ap->a_vp;
+       struct syncer_ctx *ctx;
+
+       ctx = vn_get_syncer(vp);
 
-       lwkt_gettoken(&syncer_token);
+       lwkt_gettoken(&ctx->sc_token);
        KKASSERT(vp->v_mount->mnt_syncer != vp);
        if (vp->v_flag & VONWORKLST) {
                LIST_REMOVE(vp, v_synclist);
                vclrflags(vp, VONWORKLST);
        }
-       lwkt_reltoken(&syncer_token);
+       lwkt_reltoken(&ctx->sc_token);
 
        return (0);
 }