Author: mm
Date: Thu May 10 10:39:45 2012
New Revision: 235222
URL: http://svn.freebsd.org/changeset/base/235222

Log:
  Import illumos changeset 13686:4bc0783f6064
  2703 add mechanism to report ZFS send progress
  
  If the zfs send command is used with the -v flag, the amount of bytes
  transmitted is reported in per second updates.
  
  References:
  https://www.illumos.org/issues/2703
  
  Obtained from:        illumos (issue #2703)
  MFC after:    2 weeks

Modified:
  head/cddl/contrib/opensolaris/cmd/zfs/zfs.8
  head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
  head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
  head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
  head/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
  head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h

Modified: head/cddl/contrib/opensolaris/cmd/zfs/zfs.8
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zfs/zfs.8 Thu May 10 10:16:21 2012        
(r235221)
+++ head/cddl/contrib/opensolaris/cmd/zfs/zfs.8 Thu May 10 10:39:45 2012        
(r235222)
@@ -20,6 +20,7 @@
 .\" Copyright (c) 2010, Sun Microsystems, Inc. All Rights Reserved.
 .\" Copyright (c) 2012 by Delphix. All rights reserved.
 .\" Copyright (c) 2012 Nexenta Systems, Inc. All Rights Reserved.
+.\" Copyright (c) 2012, Joyent, Inc. All rights reserved.
 .\" Copyright (c) 2011, Pawel Jakub Dawidek <p...@freebsd.org>
 .\"
 .\" $FreeBSD$
@@ -2280,6 +2281,7 @@ flags to determine what data will be sen
 Print machine-parsable verbose information about the stream package generated.
 .It Fl v
 Print verbose information about the stream package generated.
+This information includes a per-second report of how much data has been sent.
 .El
 .Pp
 The format of the stream is committed. You will be able to receive your streams

Modified: head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c    Thu May 10 10:16:21 
2012        (r235221)
+++ head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c    Thu May 10 10:39:45 
2012        (r235222)
@@ -24,6 +24,7 @@
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
  * Copyright 2012 Milan Jurik. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
  * Copyright (c) 2012 Martin Matuska <m...@freebsd.org>. All rights reserved.
@@ -3590,6 +3591,7 @@ zfs_do_send(int argc, char **argv)
                        if (flags.verbose)
                                extraverbose = B_TRUE;
                        flags.verbose = B_TRUE;
+                       flags.progress = B_TRUE;
                        break;
                case 'D':
                        flags.dedup = B_TRUE;

Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h    Thu May 10 
10:16:21 2012        (r235221)
+++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h    Thu May 10 
10:39:45 2012        (r235222)
@@ -25,7 +25,7 @@
  * Copyright (c) 2011 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
- * All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2012 Martin Matuska <m...@freebsd.org>. All rights reserved.
  */
 
@@ -573,6 +573,9 @@ typedef struct sendflags {
 
        /* parsable verbose output (ie. -P) */
        boolean_t parsable;
+
+       /* show progress (ie. -v) */
+       boolean_t progress;
 } sendflags_t;
 
 typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);

Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c   Thu May 
10 10:16:21 2012        (r235221)
+++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c   Thu May 
10 10:39:45 2012        (r235222)
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2012 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
  */
@@ -40,6 +41,7 @@
 #include <sys/mount.h>
 #include <pthread.h>
 #include <umem.h>
+#include <time.h>
 
 #include <libzfs.h>
 
@@ -67,6 +69,12 @@ typedef struct dedup_arg {
        libzfs_handle_t  *dedup_hdl;
 } dedup_arg_t;
 
+typedef struct progress_arg {
+       zfs_handle_t *pa_zhp;
+       int pa_fd;
+       boolean_t pa_parsable;
+} progress_arg_t;
+
 typedef struct dataref {
        uint64_t ref_guid;
        uint64_t ref_object;
@@ -786,7 +794,7 @@ typedef struct send_dump_data {
        char prevsnap[ZFS_MAXNAMELEN];
        uint64_t prevsnap_obj;
        boolean_t seenfrom, seento, replicate, doall, fromorigin;
-       boolean_t verbose, dryrun, parsable;
+       boolean_t verbose, dryrun, parsable, progress;
        int outfd;
        boolean_t err;
        nvlist_t *fss;
@@ -979,10 +987,60 @@ hold_for_send(zfs_handle_t *zhp, send_du
        return (error);
 }
 
+static void *
+send_progress_thread(void *arg)
+{
+       progress_arg_t *pa = arg;
+
+       zfs_cmd_t zc = { 0 };
+       zfs_handle_t *zhp = pa->pa_zhp;
+       libzfs_handle_t *hdl = zhp->zfs_hdl;
+       unsigned long long bytes;
+       char buf[16];
+
+       time_t t;
+       struct tm *tm;
+
+       assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+       (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+       if (!pa->pa_parsable)
+               (void) fprintf(stderr, "TIME        SENT   SNAPSHOT\n");
+
+       /*
+        * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
+        */
+       for (;;) {
+               (void) sleep(1);
+
+               zc.zc_cookie = pa->pa_fd;
+               if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
+                       return ((void *)-1);
+
+               (void) time(&t);
+               tm = localtime(&t);
+               bytes = zc.zc_cookie;
+
+               if (pa->pa_parsable) {
+                       (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
+                           tm->tm_hour, tm->tm_min, tm->tm_sec,
+                           bytes, zhp->zfs_name);
+               } else {
+                       zfs_nicenum(bytes, buf, sizeof (buf));
+                       (void) fprintf(stderr, "%02d:%02d:%02d   %5s   %s\n",
+                           tm->tm_hour, tm->tm_min, tm->tm_sec,
+                           buf, zhp->zfs_name);
+               }
+       }
+}
+
 static int
 dump_snapshot(zfs_handle_t *zhp, void *arg)
 {
        send_dump_data_t *sdd = arg;
+       progress_arg_t pa = { 0 };
+       pthread_t tid;
+
        char *thissnap;
        int err;
        boolean_t isfromsnap, istosnap, fromorigin;
@@ -1100,8 +1158,29 @@ dump_snapshot(zfs_handle_t *zhp, void *a
        }
 
        if (!sdd->dryrun) {
+               /*
+                * If progress reporting is requested, spawn a new thread to
+                * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
+                */
+               if (sdd->progress) {
+                       pa.pa_zhp = zhp;
+                       pa.pa_fd = sdd->outfd;
+                       pa.pa_parsable = sdd->parsable;
+
+                       if (err = pthread_create(&tid, NULL,
+                           send_progress_thread, &pa)) {
+                               zfs_close(zhp);
+                               return (err);
+                       }
+               }
+
                err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
                    fromorigin, sdd->outfd, sdd->debugnv);
+
+               if (sdd->progress) {
+                       (void) pthread_cancel(tid);
+                       (void) pthread_join(tid, NULL);
+               }
        }
 
        (void) strcpy(sdd->prevsnap, thissnap);
@@ -1451,12 +1530,13 @@ zfs_send(zfs_handle_t *zhp, const char *
        sdd.fsavl = fsavl;
        sdd.verbose = flags->verbose;
        sdd.parsable = flags->parsable;
+       sdd.progress = flags->progress;
        sdd.dryrun = flags->dryrun;
        sdd.filter_cb = filter_func;
        sdd.filter_cb_arg = cb_arg;
        if (debugnvp)
                sdd.debugnv = *debugnvp;
-       if (holdsnaps) {
+       if (holdsnaps || flags->progress) {
                ++holdseq;
                (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
                    ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);

Modified: head/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h Thu May 
10 10:16:21 2012        (r235221)
+++ head/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h Thu May 
10 10:39:45 2012        (r235222)
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 #ifndef _SYS_ZFS_CONTEXT_H
@@ -228,6 +229,7 @@ struct proc {
 };
 
 extern struct proc p0;
+#define        curproc         (&p0)
 
 #define        PS_NONE         -1
 

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c      Thu May 
10 10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c      Thu May 
10 10:39:45 2012        (r235222)
@@ -25,6 +25,8 @@
 /*
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2012, Martin Matuska <m...@freebsd.org>. All rights reserved.
  */
 
 #include <sys/dmu.h>
@@ -54,38 +56,15 @@ int zfs_send_corrupt_data = B_FALSE;
 
 static char *dmu_recv_tag = "dmu_recv_tag";
 
-/*
- * The list of data whose inclusion in a send stream can be pending from
- * one call to backup_cb to another.  Multiple calls to dump_free() and
- * dump_freeobjects() can be aggregated into a single DRR_FREE or
- * DRR_FREEOBJECTS replay record.
- */
-typedef enum {
-       PENDING_NONE,
-       PENDING_FREE,
-       PENDING_FREEOBJECTS
-} pendop_t;
-
-struct backuparg {
-       dmu_replay_record_t *drr;
-       kthread_t *td;
-       struct file *fp;
-       offset_t *off;
-       objset_t *os;
-       zio_cksum_t zc;
-       uint64_t toguid;
-       int err;
-       pendop_t pending_op;
-};
-
 static int
-dump_bytes(struct backuparg *ba, void *buf, int len)
+dump_bytes(dmu_sendarg_t *dsp, void *buf, int len)
 {
+       dsl_dataset_t *ds = dsp->dsa_os->os_dsl_dataset;
        struct uio auio;
        struct iovec aiov;
        ASSERT3U(len % 8, ==, 0);
 
-       fletcher_4_incremental_native(buf, len, &ba->zc);
+       fletcher_4_incremental_native(buf, len, &dsp->dsa_zc);
        aiov.iov_base = buf;
        aiov.iov_len = len;
        auio.uio_iov = &aiov;
@@ -94,24 +73,28 @@ dump_bytes(struct backuparg *ba, void *b
        auio.uio_segflg = UIO_SYSSPACE;
        auio.uio_rw = UIO_WRITE;
        auio.uio_offset = (off_t)-1;
-       auio.uio_td = ba->td;
+       auio.uio_td = dsp->dsa_td;
 #ifdef _KERNEL
-       if (ba->fp->f_type == DTYPE_VNODE)
+       if (dsp->dsa_fp->f_type == DTYPE_VNODE)
                bwillwrite();
-       ba->err = fo_write(ba->fp, &auio, ba->td->td_ucred, 0, ba->td);
+       dsp->dsa_err = fo_write(dsp->dsa_fp, &auio, dsp->dsa_td->td_ucred, 0,
+           dsp->dsa_td);
 #else
        fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__);
-       ba->err = EOPNOTSUPP;
+       dsp->dsa_err = EOPNOTSUPP;
 #endif
-       *ba->off += len;
-       return (ba->err);
+       mutex_enter(&ds->ds_sendstream_lock);
+       *dsp->dsa_off += len;
+       mutex_exit(&ds->ds_sendstream_lock);
+
+       return (dsp->dsa_err);
 }
 
 static int
-dump_free(struct backuparg *ba, uint64_t object, uint64_t offset,
+dump_free(dmu_sendarg_t *dsp, uint64_t object, uint64_t offset,
     uint64_t length)
 {
-       struct drr_free *drrf = &(ba->drr->drr_u.drr_free);
+       struct drr_free *drrf = &(dsp->dsa_drr->drr_u.drr_free);
 
        /*
         * If there is a pending op, but it's not PENDING_FREE, push it out,
@@ -120,13 +103,15 @@ dump_free(struct backuparg *ba, uint64_t
         * other DRR_FREE records.  DRR_FREEOBJECTS records can only be
         * aggregated with other DRR_FREEOBJECTS records.
         */
-       if (ba->pending_op != PENDING_NONE && ba->pending_op != PENDING_FREE) {
-               if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dsp->dsa_pending_op != PENDING_NONE &&
+           dsp->dsa_pending_op != PENDING_FREE) {
+               if (dump_bytes(dsp, dsp->dsa_drr,
+                   sizeof (dmu_replay_record_t)) != 0)
                        return (EINTR);
-               ba->pending_op = PENDING_NONE;
+               dsp->dsa_pending_op = PENDING_NONE;
        }
 
-       if (ba->pending_op == PENDING_FREE) {
+       if (dsp->dsa_pending_op == PENDING_FREE) {
                /*
                 * There should never be a PENDING_FREE if length is -1
                 * (because dump_dnode is the only place where this
@@ -144,34 +129,35 @@ dump_free(struct backuparg *ba, uint64_t
                        return (0);
                } else {
                        /* not a continuation.  Push out pending record */
-                       if (dump_bytes(ba, ba->drr,
+                       if (dump_bytes(dsp, dsp->dsa_drr,
                            sizeof (dmu_replay_record_t)) != 0)
                                return (EINTR);
-                       ba->pending_op = PENDING_NONE;
+                       dsp->dsa_pending_op = PENDING_NONE;
                }
        }
        /* create a FREE record and make it pending */
-       bzero(ba->drr, sizeof (dmu_replay_record_t));
-       ba->drr->drr_type = DRR_FREE;
+       bzero(dsp->dsa_drr, sizeof (dmu_replay_record_t));
+       dsp->dsa_drr->drr_type = DRR_FREE;
        drrf->drr_object = object;
        drrf->drr_offset = offset;
        drrf->drr_length = length;
-       drrf->drr_toguid = ba->toguid;
+       drrf->drr_toguid = dsp->dsa_toguid;
        if (length == -1ULL) {
-               if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+               if (dump_bytes(dsp, dsp->dsa_drr,
+                   sizeof (dmu_replay_record_t)) != 0)
                        return (EINTR);
        } else {
-               ba->pending_op = PENDING_FREE;
+               dsp->dsa_pending_op = PENDING_FREE;
        }
 
        return (0);
 }
 
 static int
-dump_data(struct backuparg *ba, dmu_object_type_t type,
+dump_data(dmu_sendarg_t *dsp, dmu_object_type_t type,
     uint64_t object, uint64_t offset, int blksz, const blkptr_t *bp, void 
*data)
 {
-       struct drr_write *drrw = &(ba->drr->drr_u.drr_write);
+       struct drr_write *drrw = &(dsp->dsa_drr->drr_u.drr_write);
 
 
        /*
@@ -180,19 +166,20 @@ dump_data(struct backuparg *ba, dmu_obje
         * the stream, since aggregation can't be done across operations
         * of different types.
         */
-       if (ba->pending_op != PENDING_NONE) {
-               if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dsp->dsa_pending_op != PENDING_NONE) {
+               if (dump_bytes(dsp, dsp->dsa_drr,
+                   sizeof (dmu_replay_record_t)) != 0)
                        return (EINTR);
-               ba->pending_op = PENDING_NONE;
+               dsp->dsa_pending_op = PENDING_NONE;
        }
        /* write a DATA record */
-       bzero(ba->drr, sizeof (dmu_replay_record_t));
-       ba->drr->drr_type = DRR_WRITE;
+       bzero(dsp->dsa_drr, sizeof (dmu_replay_record_t));
+       dsp->dsa_drr->drr_type = DRR_WRITE;
        drrw->drr_object = object;
        drrw->drr_type = type;
        drrw->drr_offset = offset;
        drrw->drr_length = blksz;
-       drrw->drr_toguid = ba->toguid;
+       drrw->drr_toguid = dsp->dsa_toguid;
        drrw->drr_checksumtype = BP_GET_CHECKSUM(bp);
        if (zio_checksum_table[drrw->drr_checksumtype].ci_dedup)
                drrw->drr_checksumflags |= DRR_CHECKSUM_DEDUP;
@@ -201,42 +188,43 @@ dump_data(struct backuparg *ba, dmu_obje
        DDK_SET_COMPRESS(&drrw->drr_key, BP_GET_COMPRESS(bp));
        drrw->drr_key.ddk_cksum = bp->blk_cksum;
 
-       if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dump_bytes(dsp, dsp->dsa_drr, sizeof (dmu_replay_record_t)) != 0)
                return (EINTR);
-       if (dump_bytes(ba, data, blksz) != 0)
+       if (dump_bytes(dsp, data, blksz) != 0)
                return (EINTR);
        return (0);
 }
 
 static int
-dump_spill(struct backuparg *ba, uint64_t object, int blksz, void *data)
+dump_spill(dmu_sendarg_t *dsp, uint64_t object, int blksz, void *data)
 {
-       struct drr_spill *drrs = &(ba->drr->drr_u.drr_spill);
+       struct drr_spill *drrs = &(dsp->dsa_drr->drr_u.drr_spill);
 
-       if (ba->pending_op != PENDING_NONE) {
-               if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dsp->dsa_pending_op != PENDING_NONE) {
+               if (dump_bytes(dsp, dsp->dsa_drr,
+                   sizeof (dmu_replay_record_t)) != 0)
                        return (EINTR);
-               ba->pending_op = PENDING_NONE;
+               dsp->dsa_pending_op = PENDING_NONE;
        }
 
        /* write a SPILL record */
-       bzero(ba->drr, sizeof (dmu_replay_record_t));
-       ba->drr->drr_type = DRR_SPILL;
+       bzero(dsp->dsa_drr, sizeof (dmu_replay_record_t));
+       dsp->dsa_drr->drr_type = DRR_SPILL;
        drrs->drr_object = object;
        drrs->drr_length = blksz;
-       drrs->drr_toguid = ba->toguid;
+       drrs->drr_toguid = dsp->dsa_toguid;
 
-       if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)))
+       if (dump_bytes(dsp, dsp->dsa_drr, sizeof (dmu_replay_record_t)))
                return (EINTR);
-       if (dump_bytes(ba, data, blksz))
+       if (dump_bytes(dsp, data, blksz))
                return (EINTR);
        return (0);
 }
 
 static int
-dump_freeobjects(struct backuparg *ba, uint64_t firstobj, uint64_t numobjs)
+dump_freeobjects(dmu_sendarg_t *dsp, uint64_t firstobj, uint64_t numobjs)
 {
-       struct drr_freeobjects *drrfo = &(ba->drr->drr_u.drr_freeobjects);
+       struct drr_freeobjects *drrfo = &(dsp->dsa_drr->drr_u.drr_freeobjects);
 
        /*
         * If there is a pending op, but it's not PENDING_FREEOBJECTS,
@@ -245,13 +233,14 @@ dump_freeobjects(struct backuparg *ba, u
         * aggregated with other DRR_FREE records.  DRR_FREEOBJECTS records
         * can only be aggregated with other DRR_FREEOBJECTS records.
         */
-       if (ba->pending_op != PENDING_NONE &&
-           ba->pending_op != PENDING_FREEOBJECTS) {
-               if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dsp->dsa_pending_op != PENDING_NONE &&
+           dsp->dsa_pending_op != PENDING_FREEOBJECTS) {
+               if (dump_bytes(dsp, dsp->dsa_drr,
+                   sizeof (dmu_replay_record_t)) != 0)
                        return (EINTR);
-               ba->pending_op = PENDING_NONE;
+               dsp->dsa_pending_op = PENDING_NONE;
        }
-       if (ba->pending_op == PENDING_FREEOBJECTS) {
+       if (dsp->dsa_pending_op == PENDING_FREEOBJECTS) {
                /*
                 * See whether this free object array can be aggregated
                 * with pending one
@@ -261,42 +250,43 @@ dump_freeobjects(struct backuparg *ba, u
                        return (0);
                } else {
                        /* can't be aggregated.  Push out pending record */
-                       if (dump_bytes(ba, ba->drr,
+                       if (dump_bytes(dsp, dsp->dsa_drr,
                            sizeof (dmu_replay_record_t)) != 0)
                                return (EINTR);
-                       ba->pending_op = PENDING_NONE;
+                       dsp->dsa_pending_op = PENDING_NONE;
                }
        }
 
        /* write a FREEOBJECTS record */
-       bzero(ba->drr, sizeof (dmu_replay_record_t));
-       ba->drr->drr_type = DRR_FREEOBJECTS;
+       bzero(dsp->dsa_drr, sizeof (dmu_replay_record_t));
+       dsp->dsa_drr->drr_type = DRR_FREEOBJECTS;
        drrfo->drr_firstobj = firstobj;
        drrfo->drr_numobjs = numobjs;
-       drrfo->drr_toguid = ba->toguid;
+       drrfo->drr_toguid = dsp->dsa_toguid;
 
-       ba->pending_op = PENDING_FREEOBJECTS;
+       dsp->dsa_pending_op = PENDING_FREEOBJECTS;
 
        return (0);
 }
 
 static int
-dump_dnode(struct backuparg *ba, uint64_t object, dnode_phys_t *dnp)
+dump_dnode(dmu_sendarg_t *dsp, uint64_t object, dnode_phys_t *dnp)
 {
-       struct drr_object *drro = &(ba->drr->drr_u.drr_object);
+       struct drr_object *drro = &(dsp->dsa_drr->drr_u.drr_object);
 
        if (dnp == NULL || dnp->dn_type == DMU_OT_NONE)
-               return (dump_freeobjects(ba, object, 1));
+               return (dump_freeobjects(dsp, object, 1));
 
-       if (ba->pending_op != PENDING_NONE) {
-               if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dsp->dsa_pending_op != PENDING_NONE) {
+               if (dump_bytes(dsp, dsp->dsa_drr,
+                   sizeof (dmu_replay_record_t)) != 0)
                        return (EINTR);
-               ba->pending_op = PENDING_NONE;
+               dsp->dsa_pending_op = PENDING_NONE;
        }
 
        /* write an OBJECT record */
-       bzero(ba->drr, sizeof (dmu_replay_record_t));
-       ba->drr->drr_type = DRR_OBJECT;
+       bzero(dsp->dsa_drr, sizeof (dmu_replay_record_t));
+       dsp->dsa_drr->drr_type = DRR_OBJECT;
        drro->drr_object = object;
        drro->drr_type = dnp->dn_type;
        drro->drr_bonustype = dnp->dn_bonustype;
@@ -304,19 +294,19 @@ dump_dnode(struct backuparg *ba, uint64_
        drro->drr_bonuslen = dnp->dn_bonuslen;
        drro->drr_checksumtype = dnp->dn_checksum;
        drro->drr_compress = dnp->dn_compress;
-       drro->drr_toguid = ba->toguid;
+       drro->drr_toguid = dsp->dsa_toguid;
 
-       if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dump_bytes(dsp, dsp->dsa_drr, sizeof (dmu_replay_record_t)) != 0)
                return (EINTR);
 
-       if (dump_bytes(ba, DN_BONUS(dnp), P2ROUNDUP(dnp->dn_bonuslen, 8)) != 0)
+       if (dump_bytes(dsp, DN_BONUS(dnp), P2ROUNDUP(dnp->dn_bonuslen, 8)) != 0)
                return (EINTR);
 
        /* free anything past the end of the file */
-       if (dump_free(ba, object, (dnp->dn_maxblkid + 1) *
+       if (dump_free(dsp, object, (dnp->dn_maxblkid + 1) *
            (dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT), -1ULL))
                return (EINTR);
-       if (ba->err)
+       if (dsp->dsa_err)
                return (EINTR);
        return (0);
 }
@@ -330,7 +320,7 @@ static int
 backup_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf,
     const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg)
 {
-       struct backuparg *ba = arg;
+       dmu_sendarg_t *dsp = arg;
        dmu_object_type_t type = bp ? BP_GET_TYPE(bp) : DMU_OT_NONE;
        int err = 0;
 
@@ -343,10 +333,10 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
        } else if (bp == NULL && zb->zb_object == DMU_META_DNODE_OBJECT) {
                uint64_t span = BP_SPAN(dnp, zb->zb_level);
                uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT;
-               err = dump_freeobjects(ba, dnobj, span >> DNODE_SHIFT);
+               err = dump_freeobjects(dsp, dnobj, span >> DNODE_SHIFT);
        } else if (bp == NULL) {
                uint64_t span = BP_SPAN(dnp, zb->zb_level);
-               err = dump_free(ba, zb->zb_object, zb->zb_blkid * span, span);
+               err = dump_free(dsp, zb->zb_object, zb->zb_blkid * span, span);
        } else if (zb->zb_level > 0 || type == DMU_OT_OBJSET) {
                return (0);
        } else if (type == DMU_OT_DNODE) {
@@ -365,7 +355,7 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
                for (i = 0; i < blksz >> DNODE_SHIFT; i++) {
                        uint64_t dnobj = (zb->zb_blkid <<
                            (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i;
-                       err = dump_dnode(ba, dnobj, blk+i);
+                       err = dump_dnode(dsp, dnobj, blk+i);
                        if (err)
                                break;
                }
@@ -380,7 +370,7 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
                    ZIO_FLAG_CANFAIL, &aflags, zb) != 0)
                        return (EIO);
 
-               err = dump_spill(ba, zb->zb_object, blksz, abuf->b_data);
+               err = dump_spill(dsp, zb->zb_object, blksz, abuf->b_data);
                (void) arc_buf_remove_ref(abuf, &abuf);
        } else { /* it's a level-0 block of a regular object */
                uint32_t aflags = ARC_WAIT;
@@ -404,7 +394,7 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
                        }
                }
 
-               err = dump_data(ba, type, zb->zb_object, zb->zb_blkid * blksz,
+               err = dump_data(dsp, type, zb->zb_object, zb->zb_blkid * blksz,
                    blksz, bp, abuf->b_data);
                (void) arc_buf_remove_ref(abuf, &abuf);
        }
@@ -414,13 +404,13 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
 }
 
 int
-dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
-    struct file *fp, offset_t *off)
+dmu_send(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
+    int outfd, struct file *fp, offset_t *off)
 {
        dsl_dataset_t *ds = tosnap->os_dsl_dataset;
        dsl_dataset_t *fromds = fromsnap ? fromsnap->os_dsl_dataset : NULL;
        dmu_replay_record_t *drr;
-       struct backuparg ba;
+       dmu_sendarg_t *dsp;
        int err;
        uint64_t fromtxg = 0;
 
@@ -461,8 +451,10 @@ dmu_sendbackup(objset_t *tosnap, objset_
 #ifdef _KERNEL
        if (dmu_objset_type(tosnap) == DMU_OST_ZFS) {
                uint64_t version;
-               if (zfs_get_zplprop(tosnap, ZFS_PROP_VERSION, &version) != 0)
+               if (zfs_get_zplprop(tosnap, ZFS_PROP_VERSION, &version) != 0) {
+                       kmem_free(drr, sizeof (dmu_replay_record_t));
                        return (EINVAL);
+               }
                if (version == ZPL_VERSION_SA) {
                        DMU_SET_FEATUREFLAGS(
                            drr->drr_u.drr_begin.drr_versioninfo,
@@ -489,47 +481,60 @@ dmu_sendbackup(objset_t *tosnap, objset_
        if (fromorigin)
                dsl_dataset_rele(fromds, FTAG);
 
-       ba.drr = drr;
-       ba.td = curthread;
-       ba.fp = fp;
-       ba.os = tosnap;
-       ba.off = off;
-       ba.toguid = ds->ds_phys->ds_guid;
-       ZIO_SET_CHECKSUM(&ba.zc, 0, 0, 0, 0);
-       ba.pending_op = PENDING_NONE;
+       dsp = kmem_zalloc(sizeof (dmu_sendarg_t), KM_SLEEP);
 
-       if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t)) != 0) {
-               kmem_free(drr, sizeof (dmu_replay_record_t));
-               return (ba.err);
+       dsp->dsa_drr = drr;
+       dsp->dsa_outfd = outfd;
+       dsp->dsa_proc = curproc;
+       dsp->dsa_td = curthread;
+       dsp->dsa_fp = fp;
+       dsp->dsa_os = tosnap;
+       dsp->dsa_off = off;
+       dsp->dsa_toguid = ds->ds_phys->ds_guid;
+       ZIO_SET_CHECKSUM(&dsp->dsa_zc, 0, 0, 0, 0);
+       dsp->dsa_pending_op = PENDING_NONE;
+
+       mutex_enter(&ds->ds_sendstream_lock);
+       list_insert_head(&ds->ds_sendstreams, dsp);
+       mutex_exit(&ds->ds_sendstream_lock);
+
+       if (dump_bytes(dsp, drr, sizeof (dmu_replay_record_t)) != 0) {
+               err = dsp->dsa_err;
+               goto out;
        }
 
        err = traverse_dataset(ds, fromtxg, TRAVERSE_PRE | TRAVERSE_PREFETCH,
-           backup_cb, &ba);
+           backup_cb, dsp);
 
-       if (ba.pending_op != PENDING_NONE)
-               if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t)) != 0)
+       if (dsp->dsa_pending_op != PENDING_NONE)
+               if (dump_bytes(dsp, drr, sizeof (dmu_replay_record_t)) != 0)
                        err = EINTR;
 
        if (err) {
-               if (err == EINTR && ba.err)
-                       err = ba.err;
-               kmem_free(drr, sizeof (dmu_replay_record_t));
-               return (err);
+               if (err == EINTR && dsp->dsa_err)
+                       err = dsp->dsa_err;
+               goto out;
        }
 
        bzero(drr, sizeof (dmu_replay_record_t));
        drr->drr_type = DRR_END;
-       drr->drr_u.drr_end.drr_checksum = ba.zc;
-       drr->drr_u.drr_end.drr_toguid = ba.toguid;
+       drr->drr_u.drr_end.drr_checksum = dsp->dsa_zc;
+       drr->drr_u.drr_end.drr_toguid = dsp->dsa_toguid;
 
-       if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t)) != 0) {
-               kmem_free(drr, sizeof (dmu_replay_record_t));
-               return (ba.err);
+       if (dump_bytes(dsp, drr, sizeof (dmu_replay_record_t)) != 0) {
+               err = dsp->dsa_err;
+               goto out;
        }
 
+out:
+       mutex_enter(&ds->ds_sendstream_lock);
+       list_remove(&ds->ds_sendstreams, dsp);
+       mutex_exit(&ds->ds_sendstream_lock);
+
        kmem_free(drr, sizeof (dmu_replay_record_t));
+       kmem_free(dsp, sizeof (dmu_sendarg_t));
 
-       return (0);
+       return (err);
 }
 
 int

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c   Thu May 
10 10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c   Thu May 
10 10:39:45 2012        (r235222)
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2011 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
  * Portions Copyright (c) 2011 Martin Matuska <m...@freebsd.org>
@@ -32,6 +33,7 @@
 #include <sys/dsl_prop.h>
 #include <sys/dsl_synctask.h>
 #include <sys/dmu_traverse.h>
+#include <sys/dmu_impl.h>
 #include <sys/dmu_tx.h>
 #include <sys/arc.h>
 #include <sys/zio.h>
@@ -401,6 +403,8 @@ dsl_dataset_get_ref(dsl_pool_t *dp, uint
                mutex_init(&ds->ds_lock, NULL, MUTEX_DEFAULT, NULL);
                mutex_init(&ds->ds_recvlock, NULL, MUTEX_DEFAULT, NULL);
                mutex_init(&ds->ds_opening_lock, NULL, MUTEX_DEFAULT, NULL);
+               mutex_init(&ds->ds_sendstream_lock, NULL, MUTEX_DEFAULT, NULL);
+
                rw_init(&ds->ds_rwlock, 0, 0, 0);
                cv_init(&ds->ds_exclusive_cv, NULL, CV_DEFAULT, NULL);
 
@@ -408,6 +412,9 @@ dsl_dataset_get_ref(dsl_pool_t *dp, uint
                dsl_deadlist_open(&ds->ds_deadlist,
                    mos, ds->ds_phys->ds_deadlist_obj);
 
+               list_create(&ds->ds_sendstreams, sizeof (dmu_sendarg_t),
+                   offsetof(dmu_sendarg_t, dsa_link));
+
                if (err == 0) {
                        err = dsl_dir_open_obj(dp,
                            ds->ds_phys->ds_dir_obj, NULL, ds, &ds->ds_dir);

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h       Thu May 
10 10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h       Thu May 
10 10:39:45 2012        (r235222)
@@ -24,6 +24,7 @@
  */
 /*
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 /* Portions Copyright 2010 Robert Milkowski */
@@ -706,8 +707,8 @@ typedef void (*dmu_traverse_cb_t)(objset
 void dmu_traverse_objset(objset_t *os, uint64_t txg_start,
     dmu_traverse_cb_t cb, void *arg);
 
-int dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
-    struct file *fp, offset_t *off);
+int dmu_send(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
+    int outfd, struct file *fp, offset_t *off);
 int dmu_send_estimate(objset_t *tosnap, objset_t *fromsnap,
     boolean_t fromorigin, uint64_t *sizep);
 

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h  Thu May 
10 10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h  Thu May 
10 10:39:45 2012        (r235222)
@@ -21,6 +21,8 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2012, Martin Matuska <m...@freebsd.org>. All rights reserved.
  */
 
 #ifndef _SYS_DMU_IMPL_H
@@ -31,6 +33,7 @@
 #include <sys/dnode.h>
 #include <sys/kstat.h>
 #include <sys/zfs_context.h>
+#include <sys/zfs_ioctl.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -265,6 +268,33 @@ static xuio_stats_t xuio_stats = {
        atomic_add_64(&xuio_stats.stat.value.ui64, (val))
 #define        XUIOSTAT_BUMP(stat)     XUIOSTAT_INCR(stat, 1)
 
+/*
+ * The list of data whose inclusion in a send stream can be pending from
+ * one call to backup_cb to another.  Multiple calls to dump_free() and
+ * dump_freeobjects() can be aggregated into a single DRR_FREE or
+ * DRR_FREEOBJECTS replay record.
+ */
+typedef enum {
+       PENDING_NONE,
+       PENDING_FREE,
+       PENDING_FREEOBJECTS
+} dmu_pendop_t;
+
+typedef struct dmu_sendarg {
+       list_node_t dsa_link;
+       dmu_replay_record_t *dsa_drr;
+       kthread_t *dsa_td;
+       struct file *dsa_fp;
+       int dsa_outfd;
+       struct proc *dsa_proc;
+       offset_t *dsa_off;
+       objset_t *dsa_os;
+       zio_cksum_t dsa_zc;
+       uint64_t dsa_toguid;
+       int dsa_err;
+       dmu_pendop_t dsa_pending_op;
+} dmu_sendarg_t;
+
 
 #ifdef __cplusplus
 }

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h       
Thu May 10 10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h       
Thu May 10 10:39:45 2012        (r235222)
@@ -23,6 +23,7 @@
  * Copyright (c) 2011 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 #ifndef        _SYS_DSL_DATASET_H
@@ -152,6 +153,9 @@ typedef struct dsl_dataset {
        uint64_t ds_reserved;   /* cached refreservation */
        uint64_t ds_quota;      /* cached refquota */
 
+       kmutex_t ds_sendstream_lock;
+       list_t ds_sendstreams;
+
        /* Protected by ds_lock; keep at end of struct for better locality */
        char ds_snapname[MAXNAMELEN];
 } dsl_dataset_t;

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Thu May 
10 10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Thu May 
10 10:39:45 2012        (r235222)
@@ -25,6 +25,7 @@
  * Portions Copyright 2011 Martin Matuska <m...@freebsd.org>
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -57,6 +58,7 @@
 #include <sys/dsl_prop.h>
 #include <sys/dsl_deleg.h>
 #include <sys/dmu_objset.h>
+#include <sys/dmu_impl.h>
 #include <sys/sunddi.h>
 #include <sys/policy.h>
 #include <sys/zone.h>
@@ -3933,7 +3935,8 @@ zfs_ioc_send(zfs_cmd_t *zc)
                }
 
                off = fp->f_offset;
-               error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp, &off);
+               error = dmu_send(tosnap, fromsnap, zc->zc_obj,
+                   zc->zc_cookie, fp, &off);
 
                if (off >= 0 && off <= MAXOFFSET_T)
                        fp->f_offset = off;
@@ -3945,6 +3948,49 @@ zfs_ioc_send(zfs_cmd_t *zc)
        return (error);
 }
 
+/*
+ * inputs:
+ * zc_name     name of snapshot on which to report progress
+ * zc_cookie   file descriptor of send stream
+ *
+ * outputs:
+ * zc_cookie   number of bytes written in send stream thus far
+ */
+static int
+zfs_ioc_send_progress(zfs_cmd_t *zc)
+{
+       dsl_dataset_t *ds;
+       dmu_sendarg_t *dsp = NULL;
+       int error;
+
+       if ((error = dsl_dataset_hold(zc->zc_name, FTAG, &ds)) != 0)
+               return (error);
+
+       mutex_enter(&ds->ds_sendstream_lock);
+
+       /*
+        * Iterate over all the send streams currently active on this dataset.
+        * If there's one which matches the specified file descriptor _and_ the
+        * stream was started by the current process, return the progress of
+        * that stream.
+        */
+       for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL;
+           dsp = list_next(&ds->ds_sendstreams, dsp)) {
+               if (dsp->dsa_outfd == zc->zc_cookie &&
+                   dsp->dsa_proc == curproc)
+                       break;
+       }
+
+       if (dsp != NULL)
+               zc->zc_cookie = *(dsp->dsa_off);
+       else
+               error = ENOENT;
+
+       mutex_exit(&ds->ds_sendstream_lock);
+       dsl_dataset_rele(ds, FTAG);
+       return (error);
+}
+
 static int
 zfs_ioc_inject_fault(zfs_cmd_t *zc)
 {
@@ -4946,7 +4992,9 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = {
        { zfs_ioc_space_written, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
            B_TRUE },
        { zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
-           B_TRUE }
+           B_TRUE },
+       { zfs_ioc_send_progress, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
+           B_FALSE }
 };
 
 int

Modified: head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h   Thu May 10 
10:16:21 2012        (r235221)
+++ head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h   Thu May 10 
10:39:45 2012        (r235222)
@@ -23,6 +23,8 @@
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2012, Martin Matuska <m...@freebsd.org>. All rights reserved.
  */
 
 /* Portions Copyright 2010 Robert Milkowski */
@@ -794,6 +796,7 @@ typedef     unsigned long   zfs_ioc_t;
 #define        ZFS_IOC_POOL_REGUID             _IOWR('Z', 60, struct zfs_cmd)
 #define        ZFS_IOC_SPACE_WRITTEN           _IOWR('Z', 61, struct zfs_cmd)
 #define        ZFS_IOC_SPACE_SNAPS             _IOWR('Z', 62, struct zfs_cmd)
+#define        ZFS_IOC_SEND_PROGRESS           _IOWR('Z', 63, struct zfs_cmd)
 
 /*
  * Internal SPA load state.  Used by FMA diagnosis engine.
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to