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
This commit is contained in:
Martin Matuska 2012-05-10 10:39:45 +00:00
parent 3b135bc51c
commit a837775a9e
12 changed files with 320 additions and 133 deletions

View File

@ -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 <pjd@FreeBSD.org>
.\"
.\" $FreeBSD$
@ -2280,6 +2281,7 @@ flags to determine what data will be sent.
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

View File

@ -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 <pawel@dawidek.net>.
* All rights reserved.
* Copyright (c) 2012 Martin Matuska <mm@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;

View File

@ -25,7 +25,7 @@
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@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 <mm@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 *);

View File

@ -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 <pawel@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_dump_data_t *sdd)
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 *arg)
}
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 *fromsnap, const char *tosnap,
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);

View File

@ -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

View File

@ -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 <mm@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 *buf, int len)
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 object, uint64_t offset,
* 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 object, uint64_t offset,
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_object_type_t type,
* 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_object_type_t type,
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, uint64_t firstobj, uint64_t numobjs)
* 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, uint64_t firstobj, uint64_t numobjs)
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_t object, dnode_phys_t *dnp)
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, const blkptr_t *bp, arc_buf_t *pbuf,
} 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, const blkptr_t *bp, arc_buf_t *pbuf,
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, const blkptr_t *bp, arc_buf_t *pbuf,
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, const blkptr_t *bp, arc_buf_t *pbuf,
}
}
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, const blkptr_t *bp, arc_buf_t *pbuf,
}
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_t *fromsnap, boolean_t fromorigin,
#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_t *fromsnap, boolean_t fromorigin,
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;
}
kmem_free(drr, sizeof (dmu_replay_record_t));
out:
mutex_enter(&ds->ds_sendstream_lock);
list_remove(&ds->ds_sendstreams, dsp);
mutex_exit(&ds->ds_sendstream_lock);
return (0);
kmem_free(drr, sizeof (dmu_replay_record_t));
kmem_free(dsp, sizeof (dmu_sendarg_t));
return (err);
}
int

View File

@ -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 <pawel@dawidek.net>.
* All rights reserved.
* Portions Copyright (c) 2011 Martin Matuska <mm@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, uint64_t dsobj, void *tag,
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, uint64_t dsobj, void *tag,
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);

View File

@ -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_t *os, void *arg, struct blkptr *bp,
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);

View File

@ -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 <mm@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
}

View File

@ -23,6 +23,7 @@
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@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;

View File

@ -25,6 +25,7 @@
* Portions Copyright 2011 Martin Matuska <mm@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

View File

@ -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 <mm@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.