Add journaling restart support, required to produce a robust journaling
authorMatthew Dillon <dillon@dragonflybsd.org>
Wed, 13 Jul 2005 02:00:19 +0000 (02:00 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Wed, 13 Jul 2005 02:00:19 +0000 (02:00 +0000)
environment.  If a journal is writing to one stream and the stream breaks
or dies or otherwise fails, this feature gives us the ability to restart the
journaling stream on a new descriptor without losing any data.  The
journaling restart code does a shutdown() of the old descriptor, waits for
both directions to cease operation, installs a new stream descriptor,
and resets the FIFO index to the last acknowledged offset.

This can be demonstrated by opening two windows.  In the first window do:

    mountctl -a2 /usr:test | jscan -d2 stdin

Mess around a bit on /usr.  Then in the second window do:

    mountctl -r2 /usr:test | jscan -d2 stdin

The first jscan will terminate and the new jscan will pick up the stream.

sbin/mountctl/mountctl.8
sbin/mountctl/mountctl.c
sys/kern/vfs_jops.c
sys/kern/vfs_journal.c
sys/sys/journal.h
sys/sys/mountctl.h

index 98537a8..a790ea5 100644 (file)
@@ -31,7 +31,7 @@
 .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $DragonFly: src/sbin/mountctl/mountctl.8,v 1.4 2005/07/06 06:04:32 dillon Exp $
+.\" $DragonFly: src/sbin/mountctl/mountctl.8,v 1.5 2005/07/13 02:00:19 dillon Exp $
 .\"
 .Dd January 8, 2005
 .Dt MOUNTCTL 8
 .Op Fl o Ar option ...
 .Ar mountpt:tag
 .Nm
+.Fl r
+.Op Fl 2
+.Op Fl w Ar output_path
+.Op Fl x Ar filedesc
+.Ar mountpt:tag
+.Nm
 .Fl d
 .Op Ar tag/mountpt | mountpt:tag
 .Nm
@@ -95,6 +101,15 @@ kernel-implemented swap backing will be available for journals but that is
 not the case at the moment.
 .Pp
 .Nm
+.Fl r
+will restart an existing journal, directing it to a new file descriptor.
+A shutdown is sent to the old journal and the system waits for the return
+diection (if running full-duplex) to EOF.  The new descriptor is then
+installed and the FIFO index is reset to the last acknowledged transaction.
+Clients scanning a journal across such a disconnect must check for repeated
+transaction ids since some overlap between the old and new journal may occur.
+.Pp
+.Nm
 .Fl d
 will remove the specified journal(s).  A mount point, a tag, or both may be
 specified.  This function will operate on all matching journals.
@@ -124,7 +139,7 @@ or
 .Fl d
 are not specified.
 .It Fl S
-Start or restart a journal, equivalent to the 'start' keyword.
+Start a stopped journal, equivalent to the 'start' keyword.
 This option implies
 .Fl m .
 .It Fl C
@@ -184,17 +199,16 @@ buffers larger then 4MB are not recommended.
 Specify the size of the kernel-managed swap-backed FIFO used to buffer
 overflows.
 .It Ar path=filepath
-Switch the journal's output stream to a new file.  This feature is typically
-used to restart a dead stream. 
+Specify where the journal's output stream should be directed.  
 Note that the
 .Fl w
 option is equivalent to specifying the path option.  Both should not be 
 specified.
 .It Ar fd=filedesc
-Switch the journal's output stream to a file descriptor specified by number.
-Use file descriptor 1 if you wish to reopen the journal to the current
-stdout.  This feature is typically used to restart a dead stream (for example
-if a TCP stream fails).
+Specify where the journal's output stream should be directed by handing over
+a file desciptor.
+Use file descriptor 1 if you wish to output the journal to the current
+stdout. 
 Note that the
 .Fl w
 option is equivalent to specifying the path option.  Both should not be 
index b797539..77622ce 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.5 2005/07/06 06:04:32 dillon Exp $
+ * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.6 2005/07/13 02:00:19 dillon Exp $
  */
 /*
  * This utility implements the userland mountctl command which is used to
@@ -61,6 +61,8 @@ static int mountctl_scan(void (*func)(const char *, const char *, int, void *),
 static void mountctl_list(const char *keyword, const char *mountpt,
                int __unused fd, void *info);
 static void mountctl_add(const char *keyword, const char *mountpt, int fd);
+static void mountctl_restart(const char *keyword, const char *mountpt, 
+               int fd, void __unused *);
 static void mountctl_delete(const char *keyword, const char *mountpt,
                int __unused fd, void __unused *);
 static void mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *);
@@ -90,6 +92,7 @@ main(int ac, char **av)
     int fopt = 0;
     int lopt = 0;
     int mopt = 0;
+    int ropt = 0;
     int mimplied = 0;
     const char *wopt = NULL;
     const char *xopt = NULL;
@@ -97,21 +100,28 @@ main(int ac, char **av)
     const char *mountpt = NULL;
     char *tmp;
 
-    while ((ch = getopt(ac, av, "2adflo:mw:x:ACFSZ")) != -1) {
+    while ((ch = getopt(ac, av, "2adflmo:rw:x:ACFSZ")) != -1) {
        switch(ch) {
        case '2':
            twoway_opt = 1;
            break;
+       case 'r':
+           ropt = 1;
+           if (aopt + dopt + lopt + mopt + ropt != 1) {
+               fprintf(stderr, "too many action options specified\n");
+               usage();
+           }
+           break;
        case 'a':
            aopt = 1;
-           if (aopt + dopt + lopt + mopt != 1) {
+           if (aopt + dopt + lopt + mopt + ropt != 1) {
                fprintf(stderr, "too many action options specified\n");
                usage();
            }
            break;
        case 'd':
            dopt = 1;
-           if (aopt + dopt + lopt + mopt != 1) {
+           if (aopt + dopt + lopt + mopt + ropt != 1) {
                fprintf(stderr, "too many action options specified\n");
                usage();
            }
@@ -121,7 +131,7 @@ main(int ac, char **av)
            break;
        case 'l':
            lopt = 1;
-           if (aopt + dopt + lopt + mopt != 1) {
+           if (aopt + dopt + lopt + mopt + ropt != 1) {
                fprintf(stderr, "too many action options specified\n");
                usage();
            }
@@ -131,7 +141,7 @@ main(int ac, char **av)
            break;
        case 'm':
            mopt = 1;
-           if (aopt + dopt + lopt + mopt != 1) {
+           if (aopt + dopt + lopt + mopt + ropt != 1) {
                fprintf(stderr, "too many action options specified\n");
                usage();
            }
@@ -177,7 +187,7 @@ main(int ac, char **av)
      */
     switch(ac) {
     case 0:
-       if (aopt) {
+       if (aopt || ropt) {
            fprintf(stderr, "action requires a tag and/or mount "
                            "point to be specified\n");
            usage();
@@ -204,14 +214,14 @@ main(int ac, char **av)
     /*
      * Additional sanity checks
      */
-    if (aopt + dopt + lopt + mopt + mimplied == 0) {
+    if (aopt + dopt + lopt + mopt + ropt + mimplied == 0) {
        fprintf(stderr, "no action or implied action options were specified\n");
        usage();
     }
-    if (mimplied && aopt + dopt + lopt == 0)
+    if (mimplied && aopt + dopt + lopt + ropt == 0)
        mopt = 1;
-    if ((wopt || xopt) && !(aopt || mopt)) {
-       fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a\n");
+    if ((wopt || xopt) && !(aopt || ropt || mopt)) {
+       fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a/-r\n");
        usage();
     }
     if (aopt && (keyword == NULL || mountpt == NULL)) {
@@ -239,7 +249,7 @@ main(int ac, char **av)
        }
     } else if (xopt) {
        fd = strtol(xopt, NULL, 0);
-    } else if (aopt) {
+    } else if (aopt || ropt) {
        fd = 1;         /* stdout default for -a */
     } else {
        fd = -1;
@@ -252,19 +262,26 @@ main(int ac, char **av)
        mountctl_scan(mountctl_list, keyword, mountpt, fd);
     if (aopt)
        mountctl_add(keyword, mountpt, fd);
+    if (ropt) {
+       ch = mountctl_scan(mountctl_restart, keyword, mountpt, fd);
+       if (ch)
+           fprintf(stderr, "%d journals restarted\n", ch);
+       else
+           fprintf(stderr, "Unable to locate any matching journals\n");
+    }
     if (dopt) {
        ch = mountctl_scan(mountctl_delete, keyword, mountpt, -1);
        if (ch)
-           printf("%d journals deleted\n", ch);
+           fprintf(stderr, "%d journals deleted\n", ch);
        else
-           printf("Unable to locate any matching journals\n");
+           fprintf(stderr, "Unable to locate any matching journals\n");
     }
     if (mopt) {
        ch = mountctl_scan(mountctl_modify, keyword, mountpt, fd);
        if (ch)
-           printf("%d journals modified\n", ch);
+           fprintf(stderr, "%d journals modified\n", ch);
        else
-           printf("Unable to locate any matching journals\n");
+           fprintf(stderr, "Unable to locate any matching journals\n");
     }
 
     return(exitCode);
@@ -487,7 +504,34 @@ mountctl_add(const char *keyword, const char *mountpt, int fd)
 }
 
 static void
-mountctl_delete(const char *keyword, const char *mountpt, int __unused fd, void __unused *info)
+mountctl_restart(const char *keyword, const char *mountpt, 
+                int fd, void __unused *info)
+{
+    struct mountctl_restart_journal joinfo;
+    int error;
+
+    /* XXX make sure descriptor is not on same filesystem as journal */
+
+    bzero(&joinfo, sizeof(joinfo));
+
+    snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
+    if (twoway_opt > 0)
+       joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
+    if (reversable_opt > 0)
+       joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
+
+    error = mountctl(mountpt, MOUNTCTL_RESTART_VFS_JOURNAL, fd,
+                       &joinfo, sizeof(joinfo), NULL, 0);
+    if (error == 0) {
+       fprintf(stderr, "%s:%s restarted\n", mountpt, joinfo.id);
+    } else {
+       fprintf(stderr, "%s:%s restart failed, error %s\n", mountpt, joinfo.id, strerror(errno));
+    }
+}
+
+static void
+mountctl_delete(const char *keyword, const char *mountpt, 
+               int __unused fd, void __unused *info)
 {
     struct mountctl_remove_journal joinfo;
     int error;
index 506133c..7352a6d 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/kern/vfs_jops.c,v 1.17 2005/07/06 06:02:22 dillon Exp $
+ * $DragonFly: src/sys/kern/vfs_jops.c,v 1.18 2005/07/13 01:58:20 dillon Exp $
  */
 /*
  * Each mount point may have zero or more independantly configured journals
@@ -88,6 +88,8 @@
 #include <sys/file.h>
 #include <sys/proc.h>
 #include <sys/msfbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
 
 #include <machine/limits.h>
 
@@ -104,14 +106,20 @@ static int journal_attach(struct mount *mp);
 static void journal_detach(struct mount *mp);
 static int journal_install_vfs_journal(struct mount *mp, struct file *fp,
                            const struct mountctl_install_journal *info);
+static int journal_restart_vfs_journal(struct mount *mp, struct file *fp,
+                           const struct mountctl_restart_journal *info);
 static int journal_remove_vfs_journal(struct mount *mp,
                            const struct mountctl_remove_journal *info);
+static int journal_restart(struct mount *mp, struct file *fp,
+                           struct journal *jo, int flags);
 static int journal_destroy(struct mount *mp, struct journal *jo, int flags);
 static int journal_resync_vfs_journal(struct mount *mp, const void *ctl);
 static int journal_status_vfs_journal(struct mount *mp,
                       const struct mountctl_status_journal *info,
                       struct mountctl_journal_ret_status *rstat,
                       int buflen, int *res);
+static void journal_create_threads(struct journal *jo);
+static void journal_destroy_threads(struct journal *jo, int flags);
 static void journal_wthread(void *info);
 static void journal_rthread(void *info);
 
@@ -199,6 +207,7 @@ journal_mountctl(struct vop_mountctl_args *ap)
            if (TAILQ_EMPTY(&mp->mnt_jlist))
                journal_detach(mp);
            break;
+       case MOUNTCTL_RESTART_VFS_JOURNAL:
        case MOUNTCTL_REMOVE_VFS_JOURNAL:
        case MOUNTCTL_RESYNC_VFS_JOURNAL:
        case MOUNTCTL_STATUS_VFS_JOURNAL:
@@ -218,6 +227,14 @@ journal_mountctl(struct vop_mountctl_args *ap)
            if (error == 0)
                error = journal_install_vfs_journal(mp, ap->a_fp, ap->a_ctl);
            break;
+       case MOUNTCTL_RESTART_VFS_JOURNAL:
+           if (ap->a_ctllen != sizeof(struct mountctl_restart_journal))
+               error = EINVAL;
+           if (error == 0 && ap->a_fp == NULL)
+               error = EBADF;
+           if (error == 0)
+               error = journal_restart_vfs_journal(mp, ap->a_fp, ap->a_ctl);
+           break;
        case MOUNTCTL_REMOVE_VFS_JOURNAL:
            if (ap->a_ctllen != sizeof(struct mountctl_remove_journal))
                error = EINVAL;
@@ -330,19 +347,7 @@ journal_install_vfs_journal(struct mount *mp, struct file *fp,
        free(jo, M_JOURNAL);
     } else {
        fhold(fp);
-       jo->flags |= MC_JOURNAL_WACTIVE;
-       lwkt_create(journal_wthread, jo, NULL, &jo->wthread,
-                       TDF_STOPREQ, -1, "journal w:%.*s", JIDMAX, jo->id);
-       lwkt_setpri(&jo->wthread, TDPRI_KERN_DAEMON);
-       lwkt_schedule(&jo->wthread);
-
-       if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX) {
-           jo->flags |= MC_JOURNAL_RACTIVE;
-           lwkt_create(journal_rthread, jo, NULL, &jo->rthread,
-                       TDF_STOPREQ, -1, "journal r:%.*s", JIDMAX, jo->id);
-           lwkt_setpri(&jo->rthread, TDPRI_KERN_DAEMON);
-           lwkt_schedule(&jo->rthread);
-       }
+       journal_create_threads(jo);
        jrecord_init(jo, &jrec, JREC_STREAMID_DISCONT);
        jrecord_write(&jrec, JTYPE_ASSOCIATE, 0);
        jrecord_done(&jrec, 0);
@@ -352,6 +357,71 @@ journal_install_vfs_journal(struct mount *mp, struct file *fp,
 }
 
 /*
+ * Restart a journal with a new descriptor.   The existing reader and writer
+ * threads are terminated and a new descriptor is associated with the
+ * journal.  The FIFO rindex is reset to xindex and the threads are then
+ * restarted.
+ */
+static int
+journal_restart_vfs_journal(struct mount *mp, struct file *fp,
+                          const struct mountctl_restart_journal *info)
+{
+    struct journal *jo;
+    int error;
+
+    TAILQ_FOREACH(jo, &mp->mnt_jlist, jentry) {
+       if (bcmp(jo->id, info->id, sizeof(jo->id)) == 0)
+           break;
+    }
+    if (jo)
+       error = journal_restart(mp, fp, jo, info->flags);
+    else
+       error = EINVAL;
+    return (error);
+}
+
+static int
+journal_restart(struct mount *mp, struct file *fp, 
+               struct journal *jo, int flags)
+{
+    /*
+     * XXX lock the jo
+     */
+
+#if 0
+    /*
+     * Record the fact that we are doing a restart in the journal.
+     * XXX it isn't safe to do this if the journal is being restarted
+     * because it was locked up and the writer thread has already exited.
+     */
+    jrecord_init(jo, &jrec, JREC_STREAMID_RESTART);
+    jrecord_write(&jrec, JTYPE_DISASSOCIATE, 0);
+    jrecord_done(&jrec, 0);
+#endif
+
+    /*
+     * Stop the reader and writer threads and clean up the current 
+     * descriptor.
+     */
+    printf("RESTART WITH FP %p KILLING %p\n", fp, jo->fp);
+    journal_destroy_threads(jo, flags);
+
+    if (jo->fp)
+       fdrop(jo->fp, curthread);
+
+    /*
+     * Associate the new descriptor, reset the FIFO index, and recreate
+     * the threads.
+     */
+    fhold(fp);
+    jo->fp = fp;
+    jo->fifo.rindex = jo->fifo.xindex;
+    journal_create_threads(jo);
+
+    return(0);
+}
+
+/*
  * Disassociate a journal from a mount point and terminate its worker thread.
  * A final termination record is written out before the file pointer is
  * dropped.
@@ -392,7 +462,6 @@ static int
 journal_destroy(struct mount *mp, struct journal *jo, int flags)
 {
     struct jrecord jrec;
-    int wcount;
 
     TAILQ_REMOVE(&mp->mnt_jlist, jo, jentry);
 
@@ -400,19 +469,8 @@ journal_destroy(struct mount *mp, struct journal *jo, int flags)
     jrecord_write(&jrec, JTYPE_DISASSOCIATE, 0);
     jrecord_done(&jrec, 0);
 
-    jo->flags |= MC_JOURNAL_STOP_REQ | (flags & MC_JOURNAL_STOP_IMM);
-    wakeup(&jo->fifo);
-    wcount = 0;
-    while (jo->flags & (MC_JOURNAL_WACTIVE | MC_JOURNAL_RACTIVE)) {
-       tsleep(jo, 0, "jwait", hz);
-       if (++wcount % 10 == 0) {
-           printf("Warning: journal %s waiting for descriptors to close\n",
-               jo->id);
-       }
-    }
-    lwkt_free_thread(&jo->wthread); /* XXX SMP */
-    if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX)
-       lwkt_free_thread(&jo->rthread); /* XXX SMP */
+    journal_destroy_threads(jo, flags);
+
     if (jo->fp)
        fdrop(jo->fp, curthread);
     if (jo->fifo.membase)
@@ -473,6 +531,51 @@ journal_status_vfs_journal(struct mount *mp,
     return(error);
 }
 
+static void
+journal_create_threads(struct journal *jo)
+{
+       jo->flags &= ~(MC_JOURNAL_STOP_REQ | MC_JOURNAL_STOP_IMM);
+       jo->flags |= MC_JOURNAL_WACTIVE;
+       lwkt_create(journal_wthread, jo, NULL, &jo->wthread,
+                       TDF_STOPREQ, -1, "journal w:%.*s", JIDMAX, jo->id);
+       lwkt_setpri(&jo->wthread, TDPRI_KERN_DAEMON);
+       lwkt_schedule(&jo->wthread);
+
+       if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX) {
+           jo->flags |= MC_JOURNAL_RACTIVE;
+           lwkt_create(journal_rthread, jo, NULL, &jo->rthread,
+                       TDF_STOPREQ, -1, "journal r:%.*s", JIDMAX, jo->id);
+           lwkt_setpri(&jo->rthread, TDPRI_KERN_DAEMON);
+           lwkt_schedule(&jo->rthread);
+       }
+}
+
+static void
+journal_destroy_threads(struct journal *jo, int flags)
+{
+    int wcount;
+
+    jo->flags |= MC_JOURNAL_STOP_REQ | (flags & MC_JOURNAL_STOP_IMM);
+    wakeup(&jo->fifo);
+    wcount = 0;
+    while (jo->flags & (MC_JOURNAL_WACTIVE | MC_JOURNAL_RACTIVE)) {
+       tsleep(jo, 0, "jwait", hz);
+       if (++wcount % 10 == 0) {
+           printf("Warning: journal %s waiting for descriptors to close\n",
+               jo->id);
+       }
+    }
+
+    /*
+     * XXX SMP - threads should move to cpu requesting the restart or
+     * termination before finishing up to properly interlock.
+     */
+    tsleep(jo, 0, "jwait", hz);
+    lwkt_free_thread(&jo->wthread);
+    if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX)
+       lwkt_free_thread(&jo->rthread);
+}
+
 /*
  * The per-journal worker thread is responsible for writing out the
  * journal's FIFO to the target stream.
@@ -598,6 +701,7 @@ journal_wthread(void *info)
            }
        }
     }
+    fp_shutdown(jo->fp, SHUT_WR);
     jo->flags &= ~MC_JOURNAL_WACTIVE;
     wakeup(jo);
     wakeup(&jo->fifo.windex);
index 90f454a..2833430 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/kern/vfs_journal.c,v 1.17 2005/07/06 06:02:22 dillon Exp $
+ * $DragonFly: src/sys/kern/vfs_journal.c,v 1.18 2005/07/13 01:58:20 dillon Exp $
  */
 /*
  * Each mount point may have zero or more independantly configured journals
@@ -88,6 +88,8 @@
 #include <sys/file.h>
 #include <sys/proc.h>
 #include <sys/msfbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
 
 #include <machine/limits.h>
 
@@ -104,14 +106,20 @@ static int journal_attach(struct mount *mp);
 static void journal_detach(struct mount *mp);
 static int journal_install_vfs_journal(struct mount *mp, struct file *fp,
                            const struct mountctl_install_journal *info);
+static int journal_restart_vfs_journal(struct mount *mp, struct file *fp,
+                           const struct mountctl_restart_journal *info);
 static int journal_remove_vfs_journal(struct mount *mp,
                            const struct mountctl_remove_journal *info);
+static int journal_restart(struct mount *mp, struct file *fp,
+                           struct journal *jo, int flags);
 static int journal_destroy(struct mount *mp, struct journal *jo, int flags);
 static int journal_resync_vfs_journal(struct mount *mp, const void *ctl);
 static int journal_status_vfs_journal(struct mount *mp,
                       const struct mountctl_status_journal *info,
                       struct mountctl_journal_ret_status *rstat,
                       int buflen, int *res);
+static void journal_create_threads(struct journal *jo);
+static void journal_destroy_threads(struct journal *jo, int flags);
 static void journal_wthread(void *info);
 static void journal_rthread(void *info);
 
@@ -199,6 +207,7 @@ journal_mountctl(struct vop_mountctl_args *ap)
            if (TAILQ_EMPTY(&mp->mnt_jlist))
                journal_detach(mp);
            break;
+       case MOUNTCTL_RESTART_VFS_JOURNAL:
        case MOUNTCTL_REMOVE_VFS_JOURNAL:
        case MOUNTCTL_RESYNC_VFS_JOURNAL:
        case MOUNTCTL_STATUS_VFS_JOURNAL:
@@ -218,6 +227,14 @@ journal_mountctl(struct vop_mountctl_args *ap)
            if (error == 0)
                error = journal_install_vfs_journal(mp, ap->a_fp, ap->a_ctl);
            break;
+       case MOUNTCTL_RESTART_VFS_JOURNAL:
+           if (ap->a_ctllen != sizeof(struct mountctl_restart_journal))
+               error = EINVAL;
+           if (error == 0 && ap->a_fp == NULL)
+               error = EBADF;
+           if (error == 0)
+               error = journal_restart_vfs_journal(mp, ap->a_fp, ap->a_ctl);
+           break;
        case MOUNTCTL_REMOVE_VFS_JOURNAL:
            if (ap->a_ctllen != sizeof(struct mountctl_remove_journal))
                error = EINVAL;
@@ -330,19 +347,7 @@ journal_install_vfs_journal(struct mount *mp, struct file *fp,
        free(jo, M_JOURNAL);
     } else {
        fhold(fp);
-       jo->flags |= MC_JOURNAL_WACTIVE;
-       lwkt_create(journal_wthread, jo, NULL, &jo->wthread,
-                       TDF_STOPREQ, -1, "journal w:%.*s", JIDMAX, jo->id);
-       lwkt_setpri(&jo->wthread, TDPRI_KERN_DAEMON);
-       lwkt_schedule(&jo->wthread);
-
-       if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX) {
-           jo->flags |= MC_JOURNAL_RACTIVE;
-           lwkt_create(journal_rthread, jo, NULL, &jo->rthread,
-                       TDF_STOPREQ, -1, "journal r:%.*s", JIDMAX, jo->id);
-           lwkt_setpri(&jo->rthread, TDPRI_KERN_DAEMON);
-           lwkt_schedule(&jo->rthread);
-       }
+       journal_create_threads(jo);
        jrecord_init(jo, &jrec, JREC_STREAMID_DISCONT);
        jrecord_write(&jrec, JTYPE_ASSOCIATE, 0);
        jrecord_done(&jrec, 0);
@@ -352,6 +357,71 @@ journal_install_vfs_journal(struct mount *mp, struct file *fp,
 }
 
 /*
+ * Restart a journal with a new descriptor.   The existing reader and writer
+ * threads are terminated and a new descriptor is associated with the
+ * journal.  The FIFO rindex is reset to xindex and the threads are then
+ * restarted.
+ */
+static int
+journal_restart_vfs_journal(struct mount *mp, struct file *fp,
+                          const struct mountctl_restart_journal *info)
+{
+    struct journal *jo;
+    int error;
+
+    TAILQ_FOREACH(jo, &mp->mnt_jlist, jentry) {
+       if (bcmp(jo->id, info->id, sizeof(jo->id)) == 0)
+           break;
+    }
+    if (jo)
+       error = journal_restart(mp, fp, jo, info->flags);
+    else
+       error = EINVAL;
+    return (error);
+}
+
+static int
+journal_restart(struct mount *mp, struct file *fp, 
+               struct journal *jo, int flags)
+{
+    /*
+     * XXX lock the jo
+     */
+
+#if 0
+    /*
+     * Record the fact that we are doing a restart in the journal.
+     * XXX it isn't safe to do this if the journal is being restarted
+     * because it was locked up and the writer thread has already exited.
+     */
+    jrecord_init(jo, &jrec, JREC_STREAMID_RESTART);
+    jrecord_write(&jrec, JTYPE_DISASSOCIATE, 0);
+    jrecord_done(&jrec, 0);
+#endif
+
+    /*
+     * Stop the reader and writer threads and clean up the current 
+     * descriptor.
+     */
+    printf("RESTART WITH FP %p KILLING %p\n", fp, jo->fp);
+    journal_destroy_threads(jo, flags);
+
+    if (jo->fp)
+       fdrop(jo->fp, curthread);
+
+    /*
+     * Associate the new descriptor, reset the FIFO index, and recreate
+     * the threads.
+     */
+    fhold(fp);
+    jo->fp = fp;
+    jo->fifo.rindex = jo->fifo.xindex;
+    journal_create_threads(jo);
+
+    return(0);
+}
+
+/*
  * Disassociate a journal from a mount point and terminate its worker thread.
  * A final termination record is written out before the file pointer is
  * dropped.
@@ -392,7 +462,6 @@ static int
 journal_destroy(struct mount *mp, struct journal *jo, int flags)
 {
     struct jrecord jrec;
-    int wcount;
 
     TAILQ_REMOVE(&mp->mnt_jlist, jo, jentry);
 
@@ -400,19 +469,8 @@ journal_destroy(struct mount *mp, struct journal *jo, int flags)
     jrecord_write(&jrec, JTYPE_DISASSOCIATE, 0);
     jrecord_done(&jrec, 0);
 
-    jo->flags |= MC_JOURNAL_STOP_REQ | (flags & MC_JOURNAL_STOP_IMM);
-    wakeup(&jo->fifo);
-    wcount = 0;
-    while (jo->flags & (MC_JOURNAL_WACTIVE | MC_JOURNAL_RACTIVE)) {
-       tsleep(jo, 0, "jwait", hz);
-       if (++wcount % 10 == 0) {
-           printf("Warning: journal %s waiting for descriptors to close\n",
-               jo->id);
-       }
-    }
-    lwkt_free_thread(&jo->wthread); /* XXX SMP */
-    if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX)
-       lwkt_free_thread(&jo->rthread); /* XXX SMP */
+    journal_destroy_threads(jo, flags);
+
     if (jo->fp)
        fdrop(jo->fp, curthread);
     if (jo->fifo.membase)
@@ -473,6 +531,51 @@ journal_status_vfs_journal(struct mount *mp,
     return(error);
 }
 
+static void
+journal_create_threads(struct journal *jo)
+{
+       jo->flags &= ~(MC_JOURNAL_STOP_REQ | MC_JOURNAL_STOP_IMM);
+       jo->flags |= MC_JOURNAL_WACTIVE;
+       lwkt_create(journal_wthread, jo, NULL, &jo->wthread,
+                       TDF_STOPREQ, -1, "journal w:%.*s", JIDMAX, jo->id);
+       lwkt_setpri(&jo->wthread, TDPRI_KERN_DAEMON);
+       lwkt_schedule(&jo->wthread);
+
+       if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX) {
+           jo->flags |= MC_JOURNAL_RACTIVE;
+           lwkt_create(journal_rthread, jo, NULL, &jo->rthread,
+                       TDF_STOPREQ, -1, "journal r:%.*s", JIDMAX, jo->id);
+           lwkt_setpri(&jo->rthread, TDPRI_KERN_DAEMON);
+           lwkt_schedule(&jo->rthread);
+       }
+}
+
+static void
+journal_destroy_threads(struct journal *jo, int flags)
+{
+    int wcount;
+
+    jo->flags |= MC_JOURNAL_STOP_REQ | (flags & MC_JOURNAL_STOP_IMM);
+    wakeup(&jo->fifo);
+    wcount = 0;
+    while (jo->flags & (MC_JOURNAL_WACTIVE | MC_JOURNAL_RACTIVE)) {
+       tsleep(jo, 0, "jwait", hz);
+       if (++wcount % 10 == 0) {
+           printf("Warning: journal %s waiting for descriptors to close\n",
+               jo->id);
+       }
+    }
+
+    /*
+     * XXX SMP - threads should move to cpu requesting the restart or
+     * termination before finishing up to properly interlock.
+     */
+    tsleep(jo, 0, "jwait", hz);
+    lwkt_free_thread(&jo->wthread);
+    if (jo->flags & MC_JOURNAL_WANT_FULLDUPLEX)
+       lwkt_free_thread(&jo->rthread);
+}
+
 /*
  * The per-journal worker thread is responsible for writing out the
  * journal's FIFO to the target stream.
@@ -598,6 +701,7 @@ journal_wthread(void *info)
            }
        }
     }
+    fp_shutdown(jo->fp, SHUT_WR);
     jo->flags &= ~MC_JOURNAL_WACTIVE;
     wakeup(jo);
     wakeup(&jo->fifo.windex);
index 8912424..9f6271c 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/sys/journal.h,v 1.5 2005/07/06 06:02:23 dillon Exp $
+ * $DragonFly: src/sys/sys/journal.h,v 1.6 2005/07/13 01:58:23 dillon Exp $
  */
 
 #ifndef _SYS_JOURNAL_H_
@@ -152,7 +152,8 @@ struct journal_ackrecord {
 #define JREC_STREAMID_DISCONT  0x0002  /* discontinuity */
 #define JREC_STREAMID_ANNOTATE 0x0003  /* annotation */
 #define JREC_STREAMID_ACK      0x0004  /* acknowledgement */
-                               /* 0x0005-0x007F reserved by DragonFly */
+#define JREC_STREAMID_RESTART  0x0005  /* disctoninuity - journal restart */
+                               /* 0x0006-0x007F reserved by DragonFly */
                                /* 0x0080-0x00FF for third party use */
 #define JREC_STREAMID_JMIN     0x0100  /* lowest allowed general id */
 #define JREC_STREAMID_JMAX     0x2000  /* (one past the highest allowed id) */
index d6dc846..d7f1fd9 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/sys/mountctl.h,v 1.8 2005/07/06 06:02:23 dillon Exp $
+ * $DragonFly: src/sys/sys/mountctl.h,v 1.9 2005/07/13 01:58:23 dillon Exp $
  */
 
 #ifndef _SYS_MOUNTCTL_H_
@@ -50,6 +50,7 @@
 #define MOUNTCTL_REMOVE_VFS_JOURNAL    2
 #define MOUNTCTL_RESYNC_VFS_JOURNAL    3
 #define MOUNTCTL_STATUS_VFS_JOURNAL    4
+#define MOUNTCTL_RESTART_VFS_JOURNAL   5
 
 #define MOUNTCTL_INSTALL_BLK_JOURNAL   8
 #define MOUNTCTL_REMOVE_BLK_JOURNAL    9
@@ -80,6 +81,12 @@ struct mountctl_install_journal {
 #define MC_JOURNAL_WANT_REVERSABLE     0x00020000      /* reversable stream */
 #define MC_JOURNAL_WANT_FULLDUPLEX     0x00040000      /* has ack stream */
 
+struct mountctl_restart_journal {
+       char    id[JIDMAX];
+       int     flags;
+       int     unused01;
+};
+
 struct mountctl_remove_journal {
        char    id[JIDMAX];
        int     flags;