aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/module/zfs/dmu_recv.c
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2023-01-25 18:50:29 +0000
committerMartin Matuska <mm@FreeBSD.org>2023-01-25 18:50:29 +0000
commit15f0b8c309dea1dcb14d3e374686576ff68ac43f (patch)
tree8deb5ea3d3e71ae088585ae5d9081d647edf2b3c /sys/contrib/openzfs/module/zfs/dmu_recv.c
parentda52fc464a4c80fdbed551824064d203831b4e7b (diff)
parent9cd71c8604d52def22ffaddc35755712f0fb9349 (diff)
downloadsrc-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.c47
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);
}