X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=module%2Fzfs%2Fdmu_send.c;h=fad770e275c0a87e33b1a0af8bdb25be017a171f;hb=ab26409db753bb087842ab6f1af943f3386c764f;hp=6b00b73b43be124143e7582da4ba1edcefb04f1b;hpb=428870ff734fdaccc342b33fc53cf94724409a46;p=zfs.git diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index 6b00b73..fad770e 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -21,6 +21,9 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ #include #include @@ -42,6 +45,7 @@ #include #include #include +#include static char *dmu_recv_tag = "dmu_recv_tag"; @@ -573,6 +577,14 @@ recv_existing_check(void *arg1, void *arg2, dmu_tx_t *tx) if (!rbsa->force && dsl_dataset_modified_since_lastsnap(ds)) return (ETXTBSY); + /* new snapshot name must not exist */ + err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset, + ds->ds_phys->ds_snapnames_zapobj, rbsa->tosnap, 8, 1, &val); + if (err == 0) + return (EEXIST); + if (err != ENOENT) + return (err); + if (rbsa->fromguid) { /* if incremental, most recent snapshot must match fromguid */ if (ds->ds_prev == NULL) @@ -620,13 +632,6 @@ recv_existing_check(void *arg1, void *arg2, dmu_tx_t *tx) if (err != ENOENT) return (err); - /* new snapshot name must not exist */ - err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset, - ds->ds_phys->ds_snapnames_zapobj, rbsa->tosnap, 8, 1, &val); - if (err == 0) - return (EEXIST); - if (err != ENOENT) - return (err); return (0); } @@ -661,7 +666,6 @@ recv_existing_sync(void *arg1, void *arg2, dmu_tx_t *tx) dp->dp_spa, tx, "dataset = %lld", dsobj); } - static boolean_t dmu_recv_verify_features(dsl_dataset_t *ds, struct drr_begin *drrb) { @@ -786,7 +790,7 @@ dmu_recv_begin(char *tofs, char *tosnap, char *top_ds, struct drr_begin *drrb, return (err); if (dmu_recv_verify_features(ds, drrb)) { - dsl_dataset_rele(ds, dmu_recv_tag); + dsl_dataset_rele(ds, FTAG); return (ENOTSUP); } @@ -810,7 +814,7 @@ struct restorearg { uint64_t voff; int bufsize; /* amount of memory allocated for buf */ zio_cksum_t cksum; - avl_tree_t guid_to_ds_map; + avl_tree_t *guid_to_ds_map; }; typedef struct guid_map_entry { @@ -832,59 +836,19 @@ guid_compare(const void *arg1, const void *arg2) return (0); } -/* - * This function is a callback used by dmu_objset_find() (which - * enumerates the object sets) to build an avl tree that maps guids - * to datasets. The resulting table is used when processing DRR_WRITE_BYREF - * send stream records. These records, which are used in dedup'ed - * streams, do not contain data themselves, but refer to a copy - * of the data block that has already been written because it was - * earlier in the stream. That previous copy is identified by the - * guid of the dataset with the referenced data. - */ -int -find_ds_by_guid(const char *name, void *arg) +static void +free_guid_map_onexit(void *arg) { - avl_tree_t *guid_map = arg; - dsl_dataset_t *ds, *snapds; + avl_tree_t *ca = arg; + void *cookie = NULL; guid_map_entry_t *gmep; - dsl_pool_t *dp; - int err; - uint64_t lastobj, firstobj; - if (dsl_dataset_hold(name, FTAG, &ds) != 0) - return (0); - - dp = ds->ds_dir->dd_pool; - rw_enter(&dp->dp_config_rwlock, RW_READER); - firstobj = ds->ds_dir->dd_phys->dd_origin_obj; - lastobj = ds->ds_phys->ds_prev_snap_obj; - - while (lastobj != firstobj) { - err = dsl_dataset_hold_obj(dp, lastobj, guid_map, &snapds); - if (err) { - /* - * Skip this snapshot and move on. It's not - * clear why this would ever happen, but the - * remainder of the snapshot streadm can be - * processed. - */ - rw_exit(&dp->dp_config_rwlock); - dsl_dataset_rele(ds, FTAG); - return (0); - } - - gmep = kmem_alloc(sizeof (guid_map_entry_t), KM_SLEEP); - gmep->guid = snapds->ds_phys->ds_guid; - gmep->gme_ds = snapds; - avl_add(guid_map, gmep); - lastobj = snapds->ds_phys->ds_prev_snap_obj; + while ((gmep = avl_destroy_nodes(ca, &cookie)) != NULL) { + dsl_dataset_rele(gmep->gme_ds, ca); + kmem_free(gmep, sizeof (guid_map_entry_t)); } - - rw_exit(&dp->dp_config_rwlock); - dsl_dataset_rele(ds, FTAG); - - return (0); + avl_destroy(ca); + kmem_free(ca, sizeof (avl_tree_t)); } static void * @@ -921,7 +885,7 @@ restore_read(struct restorearg *ra, int len) return (rv); } -static void +noinline static void backup_byteswap(dmu_replay_record_t *drr) { #define DO64(X) (drr->drr_u.X = BSWAP_64(drr->drr_u.X)) @@ -996,12 +960,14 @@ backup_byteswap(dmu_replay_record_t *drr) DO64(drr_end.drr_checksum.zc_word[3]); DO64(drr_end.drr_toguid); break; + default: + break; } #undef DO64 #undef DO32 } -static int +noinline static int restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro) { int err; @@ -1085,7 +1051,7 @@ restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro) } /* ARGSUSED */ -static int +noinline static int restore_freeobjects(struct restorearg *ra, objset_t *os, struct drr_freeobjects *drrfo) { @@ -1109,7 +1075,7 @@ restore_freeobjects(struct restorearg *ra, objset_t *os, return (0); } -static int +noinline static int restore_write(struct restorearg *ra, objset_t *os, struct drr_write *drrw) { @@ -1173,7 +1139,7 @@ restore_write_byref(struct restorearg *ra, objset_t *os, */ if (drrwbr->drr_toguid != drrwbr->drr_refguid) { gmesrch.guid = drrwbr->drr_refguid; - if ((gmep = avl_find(&ra->guid_to_ds_map, &gmesrch, + if ((gmep = avl_find(ra->guid_to_ds_map, &gmesrch, &where)) == NULL) { return (EINVAL); } @@ -1183,8 +1149,9 @@ restore_write_byref(struct restorearg *ra, objset_t *os, ref_os = os; } - if (err = dmu_buf_hold(ref_os, drrwbr->drr_refobject, - drrwbr->drr_refoffset, FTAG, &dbp, DMU_READ_PREFETCH)) + err = dmu_buf_hold(ref_os, drrwbr->drr_refobject, + drrwbr->drr_refoffset, FTAG, &dbp, DMU_READ_PREFETCH); + if (err) return (err); tx = dmu_tx_create(os); @@ -1254,7 +1221,7 @@ restore_spill(struct restorearg *ra, objset_t *os, struct drr_spill *drrs) } /* ARGSUSED */ -static int +noinline static int restore_free(struct restorearg *ra, objset_t *os, struct drr_free *drrf) { @@ -1276,13 +1243,13 @@ restore_free(struct restorearg *ra, objset_t *os, * NB: callers *must* call dmu_recv_end() if this succeeds. */ int -dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp) +dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp, + int cleanup_fd, uint64_t *action_handlep) { struct restorearg ra = { 0 }; dmu_replay_record_t *drr; objset_t *os; zio_cksum_t pcksum; - guid_map_entry_t *gmep; int featureflags; if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) @@ -1318,7 +1285,7 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp) ra.vp = vp; ra.voff = *voffp; ra.bufsize = 1<<20; - ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP); + ra.buf = vmem_alloc(ra.bufsize, KM_SLEEP); /* these were verified in dmu_recv_begin */ ASSERT(DMU_GET_STREAM_HDRTYPE(drc->drc_drrb->drr_versioninfo) == @@ -1336,12 +1303,37 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp) /* if this stream is dedup'ed, set up the avl tree for guid mapping */ if (featureflags & DMU_BACKUP_FEATURE_DEDUP) { - avl_create(&ra.guid_to_ds_map, guid_compare, - sizeof (guid_map_entry_t), - offsetof(guid_map_entry_t, avlnode)); - (void) dmu_objset_find(drc->drc_top_ds, find_ds_by_guid, - (void *)&ra.guid_to_ds_map, - DS_FIND_CHILDREN); + minor_t minor; + + if (cleanup_fd == -1) { + ra.err = EBADF; + goto out; + } + ra.err = zfs_onexit_fd_hold(cleanup_fd, &minor); + if (ra.err) { + cleanup_fd = -1; + goto out; + } + + if (*action_handlep == 0) { + ra.guid_to_ds_map = + kmem_alloc(sizeof (avl_tree_t), KM_SLEEP); + avl_create(ra.guid_to_ds_map, guid_compare, + sizeof (guid_map_entry_t), + offsetof(guid_map_entry_t, avlnode)); + ra.err = zfs_onexit_add_cb(minor, + free_guid_map_onexit, ra.guid_to_ds_map, + action_handlep); + if (ra.err) + goto out; + } else { + ra.err = zfs_onexit_cb_data(minor, *action_handlep, + (void **)&ra.guid_to_ds_map); + if (ra.err) + goto out; + } + + drc->drc_guid_to_ds_map = ra.guid_to_ds_map; } /* @@ -1423,6 +1415,9 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp) ASSERT(ra.err != 0); out: + if ((featureflags & DMU_BACKUP_FEATURE_DEDUP) && (cleanup_fd != -1)) + zfs_onexit_fd_rele(cleanup_fd); + if (ra.err != 0) { /* * destroy what we created, so we don't leave it in the @@ -1438,17 +1433,7 @@ out: } } - if (featureflags & DMU_BACKUP_FEATURE_DEDUP) { - void *cookie = NULL; - - while (gmep = avl_destroy_nodes(&ra.guid_to_ds_map, &cookie)) { - dsl_dataset_rele(gmep->gme_ds, &ra.guid_to_ds_map); - kmem_free(gmep, sizeof (guid_map_entry_t)); - } - avl_destroy(&ra.guid_to_ds_map); - } - - kmem_free(ra.buf, ra.bufsize); + vmem_free(ra.buf, ra.bufsize); *voffp = ra.voff; return (ra.err); } @@ -1487,6 +1472,30 @@ recv_end_sync(void *arg1, void *arg2, dmu_tx_t *tx) } static int +add_ds_to_guidmap(avl_tree_t *guid_map, dsl_dataset_t *ds) +{ + dsl_pool_t *dp = ds->ds_dir->dd_pool; + uint64_t snapobj = ds->ds_phys->ds_prev_snap_obj; + dsl_dataset_t *snapds; + guid_map_entry_t *gmep; + int err; + + ASSERT(guid_map != NULL); + + rw_enter(&dp->dp_config_rwlock, RW_READER); + err = dsl_dataset_hold_obj(dp, snapobj, guid_map, &snapds); + if (err == 0) { + gmep = kmem_alloc(sizeof (guid_map_entry_t), KM_SLEEP); + gmep->guid = snapds->ds_phys->ds_guid; + gmep->gme_ds = snapds; + avl_add(guid_map, gmep); + } + + rw_exit(&dp->dp_config_rwlock); + return (err); +} + +static int dmu_recv_existing_end(dmu_recv_cookie_t *drc) { struct recvendsyncarg resa; @@ -1526,6 +1535,8 @@ dmu_recv_existing_end(dmu_recv_cookie_t *drc) out: mutex_exit(&ds->ds_recvlock); + if (err == 0 && drc->drc_guid_to_ds_map != NULL) + (void) add_ds_to_guidmap(drc->drc_guid_to_ds_map, ds); dsl_dataset_disown(ds, dmu_recv_tag); (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag, B_FALSE); return (err); @@ -1555,6 +1566,8 @@ dmu_recv_new_end(dmu_recv_cookie_t *drc) /* clean up the fs we just recv'd into */ (void) dsl_dataset_destroy(ds, dmu_recv_tag, B_FALSE); } else { + if (drc->drc_guid_to_ds_map != NULL) + (void) add_ds_to_guidmap(drc->drc_guid_to_ds_map, ds); /* release the hold from dmu_recv_begin */ dsl_dataset_disown(ds, dmu_recv_tag); }