diff options
author | Martin Matuska <mm@FreeBSD.org> | 2023-01-25 18:50:29 +0000 |
---|---|---|
committer | Martin Matuska <mm@FreeBSD.org> | 2023-01-25 18:50:29 +0000 |
commit | 15f0b8c309dea1dcb14d3e374686576ff68ac43f (patch) | |
tree | 8deb5ea3d3e71ae088585ae5d9081d647edf2b3c /sys/contrib/openzfs/module/zfs/dmu_recv.c | |
parent | da52fc464a4c80fdbed551824064d203831b4e7b (diff) | |
parent | 9cd71c8604d52def22ffaddc35755712f0fb9349 (diff) | |
download | src-15f0b8c309dea1dcb14d3e374686576ff68ac43f.tar.gz src-15f0b8c309dea1dcb14d3e374686576ff68ac43f.zip |
Diffstat (limited to 'sys/contrib/openzfs/module/zfs/dmu_recv.c')
-rw-r--r-- | sys/contrib/openzfs/module/zfs/dmu_recv.c | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/sys/contrib/openzfs/module/zfs/dmu_recv.c b/sys/contrib/openzfs/module/zfs/dmu_recv.c index 339fb149a49f..ddaa4a5c7291 100644 --- a/sys/contrib/openzfs/module/zfs/dmu_recv.c +++ b/sys/contrib/openzfs/module/zfs/dmu_recv.c @@ -31,6 +31,7 @@ * Copyright (c) 2022 Axcient. */ +#include <sys/arc.h> #include <sys/spa_impl.h> #include <sys/dmu.h> #include <sys/dmu_impl.h> @@ -75,6 +76,12 @@ static int zfs_recv_best_effort_corrective = 0; static const void *const dmu_recv_tag = "dmu_recv_tag"; const char *const recv_clone_name = "%recv"; +typedef enum { + ORNS_NO, + ORNS_YES, + ORNS_MAYBE +} or_need_sync_t; + static int receive_read_payload_and_next_header(dmu_recv_cookie_t *ra, int len, void *buf); @@ -128,6 +135,9 @@ struct receive_writer_arg { uint8_t or_mac[ZIO_DATA_MAC_LEN]; boolean_t or_byteorder; zio_t *heal_pio; + + /* Keep track of DRR_FREEOBJECTS right after DRR_OBJECT_RANGE */ + or_need_sync_t or_need_sync; }; typedef struct dmu_recv_begin_arg { @@ -1246,19 +1256,29 @@ dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin, uint32_t payloadlen = drc->drc_drr_begin->drr_payloadlen; void *payload = NULL; + + /* + * Since OpenZFS 2.0.0, we have enforced a 64MB limit in userspace + * configurable via ZFS_SENDRECV_MAX_NVLIST. We enforce 256MB as a hard + * upper limit. Systems with less than 1GB of RAM will see a lower + * limit from `arc_all_memory() / 4`. + */ + if (payloadlen > (MIN((1U << 28), arc_all_memory() / 4))) + return (E2BIG); + if (payloadlen != 0) - payload = kmem_alloc(payloadlen, KM_SLEEP); + payload = vmem_alloc(payloadlen, KM_SLEEP); err = receive_read_payload_and_next_header(drc, payloadlen, payload); if (err != 0) { - kmem_free(payload, payloadlen); + vmem_free(payload, payloadlen); return (err); } if (payloadlen != 0) { err = nvlist_unpack(payload, payloadlen, &drc->drc_begin_nvl, KM_SLEEP); - kmem_free(payload, payloadlen); + vmem_free(payload, payloadlen); if (err != 0) { kmem_free(drc->drc_next_rrd, sizeof (*drc->drc_next_rrd)); @@ -1500,11 +1520,11 @@ receive_read(dmu_recv_cookie_t *drc, int len, void *buf) (drc->drc_featureflags & DMU_BACKUP_FEATURE_RAW) != 0); while (done < len) { - ssize_t resid; + ssize_t resid = len - done; zfs_file_t *fp = drc->drc_fp; int err = zfs_file_read(fp, (char *)buf + done, len - done, &resid); - if (resid == len - done) { + if (err == 0 && resid == len - done) { /* * Note: ECKSUM or ZFS_ERR_STREAM_TRUNCATED indicates * that the receive was interrupted and can @@ -1903,10 +1923,22 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro, /* object was freed and we are about to allocate a new one */ object_to_hold = DMU_NEW_OBJECT; } else { + /* + * If the only record in this range so far was DRR_FREEOBJECTS + * with at least one actually freed object, it's possible that + * the block will now be converted to a hole. We need to wait + * for the txg to sync to prevent races. + */ + if (rwa->or_need_sync == ORNS_YES) + txg_wait_synced(dmu_objset_pool(rwa->os), 0); + /* object is free and we are about to allocate a new one */ object_to_hold = DMU_NEW_OBJECT; } + /* Only relevant for the first object in the range */ + rwa->or_need_sync = ORNS_NO; + /* * If this is a multi-slot dnode there is a chance that this * object will expand into a slot that is already used by @@ -2100,6 +2132,9 @@ receive_freeobjects(struct receive_writer_arg *rwa, if (err != 0) return (err); + + if (rwa->or_need_sync == ORNS_MAYBE) + rwa->or_need_sync = ORNS_YES; } if (next_err != ESRCH) return (next_err); @@ -2593,6 +2628,8 @@ receive_object_range(struct receive_writer_arg *rwa, memcpy(rwa->or_mac, drror->drr_mac, ZIO_DATA_MAC_LEN); rwa->or_byteorder = byteorder; + rwa->or_need_sync = ORNS_MAYBE; + return (0); } |