mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
Wait for txg sync if the last DRR_FREEOBJECTS might result in a hole
If we receive a DRR_FREEOBJECTS as the first entry in an object range, this might end up producing a hole if the freed objects were the only existing objects in the block. If the txg starts syncing before we've processed any following DRR_OBJECT records, this leads to a possible race where the backing arc_buf_t gets its psize set to 0 in the arc_write_ready() callback while still being referenced from a dirty record in the open txg. To prevent this, we insert a txg_wait_synced call if the first record in the range was a DRR_FREEOBJECTS that actually resulted in one or more freed objects. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: David Hedberg <david.hedberg@findity.com> Sponsored by: Findity AB Closes #11893 Closes #14358
This commit is contained in:
@@ -76,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);
|
||||
|
||||
@@ -129,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 {
|
||||
@@ -1914,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
|
||||
@@ -2111,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);
|
||||
@@ -2604,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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user