Skip to content

Commit fef0eec

Browse files
behlendorftonyhutter
authored andcommitted
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 openzfs#14512 Closes openzfs#14641
1 parent b7fecb6 commit fef0eec

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
@@ -1161,7 +1161,10 @@ zvol_cdev_ioctl(struct cdev *dev, ulong_t cmd, caddr_t data,
11611161

11621162
hole = (cmd == FIOSEEKHOLE);
11631163
noff = *off;
1164+
lr = zfs_rangelock_enter(&zv->zv_rangelock, 0, UINT64_MAX,
1165+
RL_READER);
11641166
error = dmu_offset_next(zv->zv_objset, ZVOL_OBJ, hole, &noff);
1167+
zfs_rangelock_exit(lr);
11651168
*off = noff;
11661169
break;
11671170
}

module/zfs/dmu.c

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

21022102
/*
2103-
* This function is only called from zfs_holey_common() for zpl_llseek()
2104-
* in order to determine the location of holes. In order to accurately
2105-
* report holes all dirty data must be synced to disk. This causes extremely
2106-
* poor performance when seeking for holes in a dirty file. As a compromise,
2107-
* only provide hole data when the dnode is clean. When a dnode is dirty
2108-
* report the dnode as having no holes which is always a safe thing to do.
2103+
* Reports the location of data and holes in an object. In order to
2104+
* accurately report holes all dirty data must be synced to disk. This
2105+
* causes extremely poor performance when seeking for holes in a dirty file.
2106+
* As a compromise, only provide hole data when the dnode is clean. When
2107+
* a dnode is dirty report the dnode as having no holes by returning EBUSY
2108+
* which is always safe to do.
21092109
*/
21102110
int
21112111
dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
21122112
{
21132113
dnode_t *dn;
2114-
int err;
2114+
int restarted = 0, err;
21152115

21162116
restart:
21172117
err = dnode_hold(os, object, FTAG, &dn);
@@ -2123,19 +2123,23 @@ dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
21232123
if (dnode_is_dirty(dn)) {
21242124
/*
21252125
* If the zfs_dmu_offset_next_sync module option is enabled
2126-
* then strict hole reporting has been requested. Dirty
2127-
* dnodes must be synced to disk to accurately report all
2128-
* holes. When disabled dirty dnodes are reported to not
2129-
* have any holes which is always safe.
2126+
* then hole reporting has been requested. Dirty dnodes
2127+
* must be synced to disk to accurately report holes.
21302128
*
2131-
* When called by zfs_holey_common() the zp->z_rangelock
2132-
* is held to prevent zfs_write() and mmap writeback from
2133-
* re-dirtying the dnode after txg_wait_synced().
2129+
* Provided a RL_READER rangelock spanning 0-UINT64_MAX is
2130+
* held by the caller only a single restart will be required.
2131+
* We tolerate callers which do not hold the rangelock by
2132+
* returning EBUSY and not reporting holes after one restart.
21342133
*/
21352134
if (zfs_dmu_offset_next_sync) {
21362135
rw_exit(&dn->dn_struct_rwlock);
21372136
dnode_rele(dn, FTAG);
2137+
2138+
if (restarted)
2139+
return (SET_ERROR(EBUSY));
2140+
21382141
txg_wait_synced(dmu_objset_pool(os), 0);
2142+
restarted = 1;
21392143
goto restart;
21402144
}
21412145

module/zfs/zfs_vnops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ zfs_holey_common(znode_t *zp, ulong_t cmd, loff_t *off)
105105
if (zn_has_cached_data(zp))
106106
zn_flush_cached_data(zp, B_FALSE);
107107

108-
lr = zfs_rangelock_enter(&zp->z_rangelock, 0, file_sz, RL_READER);
108+
lr = zfs_rangelock_enter(&zp->z_rangelock, 0, UINT64_MAX, RL_READER);
109109
error = dmu_offset_next(ZTOZSB(zp)->z_os, zp->z_id, hole, &noff);
110110
zfs_rangelock_exit(lr);
111111

0 commit comments

Comments
 (0)