summaryrefslogtreecommitdiffstats
path: root/module/zfs/zvol.c
diff options
context:
space:
mode:
authorMatthew Ahrens <[email protected]>2017-04-14 12:59:18 -0700
committerBrian Behlendorf <[email protected]>2017-07-04 15:41:24 -0700
commit02dc43bc4615537e8e198170a0711e317c8e6dda (patch)
tree4867dbc985b020863ef8a53df03b86d3ecb89901 /module/zfs/zvol.c
parent8ca78ab00278332a877d7d95e057c0b4aca5f9ad (diff)
OpenZFS 8378 - crash due to bp in-memory modification of nopwrite block
Authored by: Matthew Ahrens <[email protected]> Reviewed by: Prakash Surya <[email protected]> Reviewed by: George Wilson <[email protected]> Approved by: Robert Mustacchi <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Ported-by: Giuseppe Di Natale <[email protected]> The problem is that zfs_get_data() supplies a stale zgd_bp to dmu_sync(), which we then nopwrite against. zfs_get_data() doesn't hold any DMU-related locks, so after it copies db_blkptr to zgd_bp, dbuf_write_ready() could change db_blkptr, and dbuf_write_done() could remove the dirty record. dmu_sync() then sees the stale BP and that the dbuf it not dirty, so it is eligible for nop-writing. The fix is for dmu_sync() to copy db_blkptr to zgd_bp after acquiring the db_mtx. We could still see a stale db_blkptr, but if it is stale then the dirty record will still exist and thus we won't attempt to nopwrite. OpenZFS-issue: https://www.illumos.org/issues/8378 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/3127742 Closes #6293
Diffstat (limited to 'module/zfs/zvol.c')
-rw-r--r--module/zfs/zvol.c10
1 files changed, 3 insertions, 7 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index 528acbc22..3e9f004ef 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -36,6 +36,7 @@
*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
+ * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
*/
/*
@@ -1018,7 +1019,6 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
zvol_state_t *zv = arg;
uint64_t offset = lr->lr_offset;
uint64_t size = lr->lr_length;
- blkptr_t *bp = &lr->lr_blkptr;
dmu_buf_t *db;
zgd_t *zgd;
int error;
@@ -1047,14 +1047,10 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
error = dmu_buf_hold_by_dnode(zv->zv_dn, offset, zgd, &db,
DMU_READ_NO_PREFETCH);
if (error == 0) {
- blkptr_t *obp = dmu_buf_get_blkptr(db);
- if (obp) {
- ASSERT(BP_IS_HOLE(bp));
- *bp = *obp;
- }
+ blkptr_t *bp = &lr->lr_blkptr;
zgd->zgd_db = db;
- zgd->zgd_bp = &lr->lr_blkptr;
+ zgd->zgd_bp = bp;
ASSERT(db != NULL);
ASSERT(db->db_offset == offset);