Skip to content

Commit 64bfa6b

Browse files
authored
Additional limits on hole reporting
Holding the zp->z_rangelock as a RL_READER over the range 0-UINT64_MAX is sufficient to prevent the dnode from being re-dirtied by concurrent writers. To avoid potentially looping multiple times for external caller which do not take the rangelock holes are not reported after the first sync. While not optimal this is always functionally correct. This change adds the missing rangelock calls on FreeBSD to zvol_cdev_ioctl(). Reviewed-by: Brian Atkinson <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #14512 Closes #14641
1 parent a604d32 commit 64bfa6b

File tree

3 files changed

+22
-15
lines changed

3 files changed

+22
-15
lines changed

module/os/freebsd/zfs/zvol_os.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,10 @@ zvol_cdev_ioctl(struct cdev *dev, ulong_t cmd, caddr_t data,
12121212

12131213
hole = (cmd == FIOSEEKHOLE);
12141214
noff = *off;
1215+
lr = zfs_rangelock_enter(&zv->zv_rangelock, 0, UINT64_MAX,
1216+
RL_READER);
12151217
error = dmu_offset_next(zv->zv_objset, ZVOL_OBJ, hole, &noff);
1218+
zfs_rangelock_exit(lr);
12161219
*off = noff;
12171220
break;
12181221
}

module/zfs/dmu.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,18 +2116,18 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp)
21162116
}
21172117

21182118
/*
2119-
* This function is only called from zfs_holey_common() for zpl_llseek()
2120-
* in order to determine the location of holes. In order to accurately
2121-
* report holes all dirty data must be synced to disk. This causes extremely
2122-
* poor performance when seeking for holes in a dirty file. As a compromise,
2123-
* only provide hole data when the dnode is clean. When a dnode is dirty
2124-
* report the dnode as having no holes which is always a safe thing to do.
2119+
* Reports the location of data and holes in an object. In order to
2120+
* accurately report holes all dirty data must be synced to disk. This
2121+
* causes extremely poor performance when seeking for holes in a dirty file.
2122+
* As a compromise, only provide hole data when the dnode is clean. When
2123+
* a dnode is dirty report the dnode as having no holes by returning EBUSY
2124+
* which is always safe to do.
21252125
*/
21262126
int
21272127
dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
21282128
{
21292129
dnode_t *dn;
2130-
int err;
2130+
int restarted = 0, err;
21312131

21322132
restart:
21332133
err = dnode_hold(os, object, FTAG, &dn);
@@ -2139,19 +2139,23 @@ dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
21392139
if (dnode_is_dirty(dn)) {
21402140
/*
21412141
* If the zfs_dmu_offset_next_sync module option is enabled
2142-
* then strict hole reporting has been requested. Dirty
2143-
* dnodes must be synced to disk to accurately report all
2144-
* holes. When disabled dirty dnodes are reported to not
2145-
* have any holes which is always safe.
2142+
* then hole reporting has been requested. Dirty dnodes
2143+
* must be synced to disk to accurately report holes.
21462144
*
2147-
* When called by zfs_holey_common() the zp->z_rangelock
2148-
* is held to prevent zfs_write() and mmap writeback from
2149-
* re-dirtying the dnode after txg_wait_synced().
2145+
* Provided a RL_READER rangelock spanning 0-UINT64_MAX is
2146+
* held by the caller only a single restart will be required.
2147+
* We tolerate callers which do not hold the rangelock by
2148+
* returning EBUSY and not reporting holes after one restart.
21502149
*/
21512150
if (zfs_dmu_offset_next_sync) {
21522151
rw_exit(&dn->dn_struct_rwlock);
21532152
dnode_rele(dn, FTAG);
2153+
2154+
if (restarted)
2155+
return (SET_ERROR(EBUSY));
2156+
21542157
txg_wait_synced(dmu_objset_pool(os), 0);
2158+
restarted = 1;
21552159
goto restart;
21562160
}
21572161

module/zfs/zfs_vnops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ zfs_holey_common(znode_t *zp, ulong_t cmd, loff_t *off)
111111
if (zn_has_cached_data(zp, 0, file_sz - 1))
112112
zn_flush_cached_data(zp, B_FALSE);
113113

114-
lr = zfs_rangelock_enter(&zp->z_rangelock, 0, file_sz, RL_READER);
114+
lr = zfs_rangelock_enter(&zp->z_rangelock, 0, UINT64_MAX, RL_READER);
115115
error = dmu_offset_next(ZTOZSB(zp)->z_os, zp->z_id, hole, &noff);
116116
zfs_rangelock_exit(lr);
117117

0 commit comments

Comments
 (0)