Print this page
*** NO COMMENTS ***

*** 21,30 **** --- 21,31 ---- /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <assert.h> #include <ctype.h> #include <errno.h>
*** 59,68 **** --- 60,71 ---- static const zio_cksum_t zero_cksum = { 0 }; typedef struct dedup_arg { int inputfd; int outputfd; + uint64_t dedup_data_sz; + boolean_t sendsize; libzfs_handle_t *dedup_hdl; } dedup_arg_t; typedef struct progress_arg { zfs_handle_t *pa_zhp;
*** 182,191 **** --- 185,212 ---- fletcher_4_incremental_native(buf, len, zc); return (write(outfd, buf, len)); } /* + * the function used by the cksummer thread that needs to know + * about the sendsize flag + */ + static int + dedup_cksum_and_write(dedup_arg_t *dda, const void *buf, uint64_t len, + zio_cksum_t *zc, int outfd) + { + int ret = len; + + dda->dedup_data_sz += len; + fletcher_4_incremental_native(buf, len, zc); + if (!dda->sendsize) + ret = (write(outfd, buf, len)); + + return (ret); + } + + /* * This function is started in a separate thread when the dedup option * has been requested. The main send thread determines the list of * snapshots to be included in the send stream and makes the ioctl calls * for each one. But instead of having the ioctl send the output to the * the output fd specified by the caller of zfs_send()), the
*** 259,269 **** fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); fflags |= (DMU_BACKUP_FEATURE_DEDUP | DMU_BACKUP_FEATURE_DEDUPPROPS); DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags); ! if (cksum_and_write(drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_COMPOUNDSTREAM && drr->drr_payloadlen != 0) { int sz = drr->drr_payloadlen; --- 280,291 ---- fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); fflags |= (DMU_BACKUP_FEATURE_DEDUP | DMU_BACKUP_FEATURE_DEDUPPROPS); DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags); ! if (dedup_cksum_and_write(dda, drr, ! sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_COMPOUNDSTREAM && drr->drr_payloadlen != 0) { int sz = drr->drr_payloadlen;
*** 273,284 **** buf = malloc(sz); } (void) ssread(buf, sz, ofp); if (ferror(stdin)) perror("fread"); ! if (cksum_and_write(buf, sz, &stream_cksum, ! outfd) == -1) goto out; } break; } --- 295,306 ---- buf = malloc(sz); } (void) ssread(buf, sz, ofp); if (ferror(stdin)) perror("fread"); ! if (dedup_cksum_and_write(dda, buf, sz, ! &stream_cksum, outfd) == -1) goto out; } break; }
*** 289,333 **** stream_cksum.zc_word[0], stream_cksum.zc_word[1], stream_cksum.zc_word[2], stream_cksum.zc_word[3]); if ((write(outfd, drr, sizeof (dmu_replay_record_t))) == -1) goto out; break; } case DRR_OBJECT: { ! if (cksum_and_write(drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; if (drro->drr_bonuslen > 0) { (void) ssread(buf, P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), ofp); ! if (cksum_and_write(buf, P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), &stream_cksum, outfd) == -1) goto out; } break; } case DRR_SPILL: { ! if (cksum_and_write(drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; (void) ssread(buf, drrs->drr_length, ofp); ! if (cksum_and_write(buf, drrs->drr_length, &stream_cksum, outfd) == -1) goto out; break; } case DRR_FREEOBJECTS: { ! if (cksum_and_write(drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; break; } --- 311,359 ---- stream_cksum.zc_word[0], stream_cksum.zc_word[1], stream_cksum.zc_word[2], stream_cksum.zc_word[3]); if ((write(outfd, drr, sizeof (dmu_replay_record_t))) == -1) goto out; + dda->dedup_data_sz += sizeof (dmu_replay_record_t); break; } case DRR_OBJECT: { ! if (dedup_cksum_and_write(dda, drr, ! sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; if (drro->drr_bonuslen > 0) { (void) ssread(buf, P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), ofp); ! if (dedup_cksum_and_write(dda, buf, P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), &stream_cksum, outfd) == -1) goto out; } break; } case DRR_SPILL: { ! if (dedup_cksum_and_write(dda, drr, ! sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; (void) ssread(buf, drrs->drr_length, ofp); ! if (dedup_cksum_and_write(dda, buf, drrs->drr_length, &stream_cksum, outfd) == -1) goto out; break; } case DRR_FREEOBJECTS: { ! if (dedup_cksum_and_write(dda, drr, ! sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; break; }
*** 388,418 **** wbr_drrr->drr_key.ddk_cksum = drrw->drr_key.ddk_cksum; wbr_drrr->drr_key.ddk_prop = drrw->drr_key.ddk_prop; ! if (cksum_and_write(&wbr_drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; } else { /* block not previously seen */ ! if (cksum_and_write(drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; ! if (cksum_and_write(buf, drrw->drr_length, &stream_cksum, outfd) == -1) goto out; } break; } case DRR_FREE: { ! if (cksum_and_write(drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; break; } --- 414,445 ---- wbr_drrr->drr_key.ddk_cksum = drrw->drr_key.ddk_cksum; wbr_drrr->drr_key.ddk_prop = drrw->drr_key.ddk_prop; ! if (dedup_cksum_and_write(dda, &wbr_drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; } else { /* block not previously seen */ ! if (dedup_cksum_and_write(dda, drr, sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; ! if (dedup_cksum_and_write(dda, buf, drrw->drr_length, &stream_cksum, outfd) == -1) goto out; } break; } case DRR_FREE: { ! if (dedup_cksum_and_write(dda, drr, ! sizeof (dmu_replay_record_t), &stream_cksum, outfd) == -1) goto out; break; }
*** 787,797 **** const char *fromsnap; const char *tosnap; char prevsnap[ZFS_MAXNAMELEN]; uint64_t prevsnap_obj; boolean_t seenfrom, seento, replicate, doall, fromorigin; ! boolean_t verbose, dryrun, parsable, progress; int outfd; boolean_t err; nvlist_t *fss; avl_tree_t *fsavl; snapfilter_cb_t *filter_cb; --- 814,827 ---- const char *fromsnap; const char *tosnap; char prevsnap[ZFS_MAXNAMELEN]; uint64_t prevsnap_obj; boolean_t seenfrom, seento, replicate, doall, fromorigin; ! boolean_t verbose, dryrun, dedup, parsable, progress; ! boolean_t sendsize; ! uint32_t hdr_send_sz; ! uint64_t send_sz; int outfd; boolean_t err; nvlist_t *fss; avl_tree_t *fsavl; snapfilter_cb_t *filter_cb;
*** 866,876 **** * Dumps a backup of the given snapshot (incremental from fromsnap if it's not * NULL) to the file descriptor specified by outfd. */ static int dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj, ! boolean_t fromorigin, int outfd, nvlist_t *debugnv) { zfs_cmd_t zc = { 0 }; libzfs_handle_t *hdl = zhp->zfs_hdl; nvlist_t *thisdbg; --- 896,907 ---- * Dumps a backup of the given snapshot (incremental from fromsnap if it's not * NULL) to the file descriptor specified by outfd. */ static int dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj, ! boolean_t fromorigin, int outfd, nvlist_t *debugnv, ! boolean_t sendsize, uint64_t *sendcounter) { zfs_cmd_t zc = { 0 }; libzfs_handle_t *hdl = zhp->zfs_hdl; nvlist_t *thisdbg;
*** 880,889 **** --- 911,922 ---- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); zc.zc_cookie = outfd; zc.zc_obj = fromorigin; zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); zc.zc_fromobj = fromsnap_obj; + zc.zc_sendsize = sendsize; + zc.zc_sendcounter = 0; VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0)); if (fromsnap && fromsnap[0] != '\0') { VERIFY(0 == nvlist_add_string(thisdbg, "fromsnap", fromsnap));
*** 933,942 **** --- 966,976 ---- default: return (zfs_standard_error(hdl, errno, errbuf)); } } + *sendcounter = (uint64_t)zc.zc_sendcounter; if (debugnv) VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg)); nvlist_free(thisdbg); return (0);
*** 1115,1129 **** } fromorigin = sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate); if (sdd->verbose) { ! uint64_t size; ! err = estimate_ioctl(zhp, sdd->prevsnap_obj, ! fromorigin, &size); ! if (sdd->parsable) { if (sdd->prevsnap[0] != '\0') { (void) fprintf(stderr, "incremental\t%s\t%s", sdd->prevsnap, zhp->zfs_name); } else { --- 1149,1161 ---- } fromorigin = sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate); + /* print out to-from and approximate size in verbose mode */ if (sdd->verbose) { ! /* print preamble */ if (sdd->parsable) { if (sdd->prevsnap[0] != '\0') { (void) fprintf(stderr, "incremental\t%s\t%s", sdd->prevsnap, zhp->zfs_name); } else {
*** 1133,1164 **** } else { (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "send from @%s to %s"), sdd->prevsnap, zhp->zfs_name); } if (err == 0) { if (sdd->parsable) { (void) fprintf(stderr, "\t%llu\n", (longlong_t)size); } else { char buf[16]; zfs_nicenum(size, buf, sizeof (buf)); ! (void) fprintf(stderr, dgettext(TEXT_DOMAIN, ! " estimated size is %s\n"), buf); } sdd->size += size; } else { (void) fprintf(stderr, "\n"); } } 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, --- 1165,1218 ---- } else { (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "send from @%s to %s"), sdd->prevsnap, zhp->zfs_name); } + + if (sdd->sendsize) { + /* + * we are going to print out the exact stream size info, + * so skip the estimate + */ + (void) fprintf(stderr, "\n"); + } else { + /* + * provide stream size estimate otherwise + */ + uint64_t size; + err = estimate_ioctl(zhp, sdd->prevsnap_obj, + fromorigin, &size); + if (err == 0) { if (sdd->parsable) { (void) fprintf(stderr, "\t%llu\n", (longlong_t)size); } else { char buf[16]; zfs_nicenum(size, buf, sizeof (buf)); ! (void) fprintf(stderr, ! dgettext(TEXT_DOMAIN, ! " estimated size is %s\n"), ! buf); } sdd->size += size; } else { + /* could not estimate */ (void) fprintf(stderr, "\n"); } } + } if (!sdd->dryrun) { + uint64_t sendcounter = 0; + boolean_t track_progress = (sdd->progress && !sdd->sendsize); + boolean_t sendsize = B_FALSE; /* * If progress reporting is requested, spawn a new thread to * poll ZFS_IOC_SEND_PROGRESS at a regular interval. */ ! if (track_progress) { pa.pa_zhp = zhp; pa.pa_fd = sdd->outfd; pa.pa_parsable = sdd->parsable; if (err = pthread_create(&tid, NULL,
*** 1166,1179 **** 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); } } --- 1220,1249 ---- zfs_close(zhp); return (err); } } + + /* + * We need to reset the sendsize flag being sent to + * kernel if sdd->dedup is set. With dedup, the file + * descriptor sent to kernel is one end of the pipe, + * and we would want the data back in the pipe for + * cksummer() to calculate the exact size of the dedup-ed + * stream. So reset the sendsize flag such that + * kernel writes to the pipe. + */ + + sendsize = sdd->dedup ? B_FALSE : sdd->sendsize; + err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj, ! fromorigin, sdd->outfd, sdd->debugnv, ! sendsize, &sendcounter); ! sdd->send_sz += sendcounter; ! ! if (track_progress) { (void) pthread_cancel(tid); (void) pthread_join(tid, NULL); } }
*** 1412,1421 **** --- 1482,1492 ---- errbuf)); } dda.outputfd = outfd; dda.inputfd = pipefd[1]; dda.dedup_hdl = zhp->zfs_hdl; + dda.sendsize = flags->sendsize; if (err = pthread_create(&tid, NULL, cksummer, &dda)) { (void) close(pipefd[0]); (void) close(pipefd[1]); zfs_error_aux(zhp->zfs_hdl, strerror(errno)); return (zfs_error(zhp->zfs_hdl,
*** 1472,1486 **** --- 1543,1559 ---- (void) snprintf(drr.drr_u.drr_begin.drr_toname, sizeof (drr.drr_u.drr_begin.drr_toname), "%s@%s", zhp->zfs_name, tosnap); drr.drr_payloadlen = buflen; err = cksum_and_write(&drr, sizeof (drr), &zc, outfd); + sdd.hdr_send_sz += sizeof (drr); /* write header nvlist */ if (err != -1 && packbuf != NULL) { err = cksum_and_write(packbuf, buflen, &zc, outfd); + sdd.hdr_send_sz += buflen; } free(packbuf); if (err == -1) { fsavl_destroy(fsavl); nvlist_free(fss);
*** 1491,1500 **** --- 1564,1574 ---- /* write end record */ bzero(&drr, sizeof (drr)); drr.drr_type = DRR_END; drr.drr_u.drr_end.drr_checksum = zc; err = write(outfd, &drr, sizeof (drr)); + sdd.hdr_send_sz += sizeof (drr); if (err == -1) { fsavl_destroy(fsavl); nvlist_free(fss); err = errno; goto stderr_out;
*** 1515,1524 **** --- 1589,1600 ---- sdd.doall = flags->doall; sdd.fromorigin = flags->fromorigin; sdd.fss = fss; sdd.fsavl = fsavl; sdd.verbose = flags->verbose; + sdd.dedup = flags->dedup; + sdd.sendsize = flags->sendsize; sdd.parsable = flags->parsable; sdd.progress = flags->progress; sdd.dryrun = flags->dryrun; sdd.filter_cb = filter_func; sdd.filter_cb_arg = cb_arg;
*** 1545,1555 **** goto stderr_out; } } else { sdd.cleanup_fd = -1; } ! if (flags->verbose) { /* * Do a verbose no-op dry run to get all the verbose output * before generating any data. Then do a non-verbose real * run to generate the streams. */ --- 1621,1631 ---- goto stderr_out; } } else { sdd.cleanup_fd = -1; } ! if (flags->verbose && !flags->sendsize) { /* * Do a verbose no-op dry run to get all the verbose output * before generating any data. Then do a non-verbose real * run to generate the streams. */
*** 1572,1581 **** --- 1648,1658 ---- nvlist_free(fss); if (flags->dedup) { (void) close(pipefd[0]); (void) pthread_join(tid, NULL); + sdd.send_sz = dda.dedup_data_sz; } if (sdd.cleanup_fd != -1) { VERIFY(0 == close(sdd.cleanup_fd)); sdd.cleanup_fd = -1;
*** 1592,1602 **** --- 1669,1694 ---- drr.drr_type = DRR_END; if (write(outfd, &drr, sizeof (drr)) == -1) { return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); } + sdd.hdr_send_sz += sizeof (drr); } + + if (flags->sendsize) { + if (flags->verbose) { + fprintf(stderr, "Send stream header size (bytes): " + "%u\n", sdd.hdr_send_sz); + fprintf(stderr, "Send stream data size (bytes): " + "%llu\n", sdd.send_sz); + fprintf(stderr, "Total send stream size (bytes): " + "%llu\n", sdd.send_sz + (uint64_t)sdd.hdr_send_sz); + } else { + fprintf(stderr, "Total send stream size (bytes): " + "%llu\n", sdd.send_sz + (uint64_t)sdd.hdr_send_sz); + } + } return (err || sdd.err); stderr_out: err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);