Skip to content

Wait for txg sync if the last DRR_FREEOBJECTS might result in a hole #14358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions module/zfs/dmu_recv.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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);

Expand Down Expand Up @@ -128,6 +134,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 {
Expand Down Expand Up @@ -1903,10 +1912,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
Expand Down Expand Up @@ -2100,6 +2121,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);
Expand Down Expand Up @@ -2593,6 +2617,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);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ tests = ['recv_dedup', 'recv_dedup_encrypted_zvol', 'rsend_001_pos',
'send-c_recv_lz4_disabled', 'send-c_mixed_compression',
'send-c_stream_size_estimate', 'send-c_embedded_blocks', 'send-c_resume',
'send-cpL_varied_recsize', 'send-c_recv_dedup', 'send-L_toggle',
'send_encrypted_incremental.ksh',
'send_encrypted_incremental.ksh', 'send_encrypted_freeobjects',
'send_encrypted_hierarchy', 'send_encrypted_props',
'send_encrypted_truncated_files', 'send_freeobjects', 'send_realloc_files',
'send_realloc_encrypted_files', 'send_spill_block', 'send_holds',
Expand Down
1 change: 1 addition & 0 deletions tests/runfiles/sanity.run
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ tests = ['recv_dedup', 'recv_dedup_encrypted_zvol', 'rsend_001_pos',
'rsend_014_pos', 'rsend_016_neg', 'send-c_verify_contents',
'send-c_volume', 'send-c_zstreamdump', 'send-c_recv_dedup',
'send-L_toggle', 'send_encrypted_hierarchy', 'send_encrypted_props',
'send_encrypted_freeobjects',
'send_encrypted_truncated_files', 'send_freeobjects', 'send_holds',
'send_mixed_raw', 'send-wR_encrypted_zvol', 'send_partial_dataset',
'send_invalid']
Expand Down
1 change: 1 addition & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/rsend/send_doall.ksh \
functional/rsend/send_encrypted_incremental.ksh \
functional/rsend/send_encrypted_files.ksh \
functional/rsend/send_encrypted_freeobjects.ksh \
functional/rsend/send_encrypted_hierarchy.ksh \
functional/rsend/send_encrypted_props.ksh \
functional/rsend/send_encrypted_truncated_files.ksh \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/bin/ksh

#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#

#
# Copyright (c) 2017 by Lawrence Livermore National Security, LLC.
# Copyright (c) 2023 by Findity AB
#

. $STF_SUITE/tests/functional/rsend/rsend.kshlib

#
# Description:
# Verify that receiving a raw encrypted stream, with a FREEOBJECTS
# removing all existing objects in a block followed by an OBJECT write
# to the same block, does not result in a panic.
#
# Strategy:
# 1. Create a new encrypted filesystem
# 2. Create file f1 as the first object in some block (here object 128)
# 3. Take snapshot A
# 4. Create file f2 as the second object in the same block (here object 129)
# 5. Delete f1
# 6. Take snapshot B
# 7. Receive a full raw encrypted send of A
# 8. Receive an incremental raw send of B
#
verify_runnable "both"

function create_object_with_num
{
file=$1
num=$2

tries=100
for ((i=0; i<$tries; i++)); do
touch $file
onum=$(ls -li $file | awk '{print $1}')

if [[ $onum -ne $num ]] ; then
rm -f $file
else
break
fi
done
if [[ $i -eq $tries ]]; then
log_fail "Failed to create object with number $num"
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little concerned this won't be entirely reliable since it depends on some assumptions about how object numbers are allocated. But as you pointed out we already do this same trick in send_freeobjects.ksh so I'm okay with doing the same thing here.

}

log_assert "FREEOBJECTS followed by OBJECT in encrypted stream does not crash"

sendds=sendencfods
recvds=recvencfods
keyfile=/$POOL/keyencfods
f1=/$POOL/$sendds/f1
f2=/$POOL/$sendds/f2

log_must eval "echo 'password' > $keyfile"

#
# xattr=sa and dnodesize=legacy for sequential object numbers, see
# note in send_freeobjects.ksh.
#
log_must zfs create -o xattr=sa -o dnodesize=legacy -o encryption=on \
-o keyformat=passphrase -o keylocation=file://$keyfile $POOL/$sendds

create_object_with_num $f1 128
log_must zfs snap $POOL/$sendds@A
create_object_with_num $f2 129
log_must rm $f1
log_must zfs snap $POOL/$sendds@B

log_must eval "zfs send -w $POOL/$sendds@A | zfs recv $POOL/$recvds"
log_must eval "zfs send -w -i $POOL/$sendds@A $POOL/$sendds@B |" \
"zfs recv $POOL/$recvds"

log_pass "FREEOBJECTS followed by OBJECT in encrypted stream did not crash"