Skip to content

Commit

Permalink
SEEK_HOLE should not block on txg_wait_synced()
Browse files Browse the repository at this point in the history
Force flushing of txg's can be painfully slow when competing for disk
io, since this is a process meant to execute asyncronously. Optimize
this path via allowing data/hole seeking if the file is clean, but if
dirty fall back to old logic. This is a compromise to disabling the
feature entirely.

Signed-off-by: Debabrata Banerjee <[email protected]>
  • Loading branch information
dbavatar committed Apr 12, 2017
1 parent 120ff39 commit 99b2905
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 13 deletions.
38 changes: 25 additions & 13 deletions module/zfs/dmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1989,32 +1989,44 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp,
zp->zp_nopwrite = nopwrite;
}

/*
* This fn is only called from zfs_holey_common for zpl_llseek
* Previously, this required flushing of all the TXG's associated with this
* file in order to scan the dnode for holes. This caused extremely poor
* performance for dirty files, for example log files. As a compromise, if a
* dnode is clean, then we can provide hole data. However if a dnode is dirty,
* simply fall back to what we would have done if we did not support seeking
* holes in this vfsop at all, which is a valid thing to do.
*/
int
dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
{
dnode_t *dn;
int i, err;
boolean_t clean = B_TRUE;

err = dnode_hold(os, object, FTAG, &dn);
if (err)
return (err);

/*
* Sync any current changes before
* we go trundling through the block pointers.
* Check if dnode is dirty
*/
for (i = 0; i < TXG_SIZE; i++) {
if (list_link_active(&dn->dn_dirty_link[i]))
break;
}
if (i != TXG_SIZE) {
dnode_rele(dn, FTAG);
txg_wait_synced(dmu_objset_pool(os), 0);
err = dnode_hold(os, object, FTAG, &dn);
if (err)
return (err);
if (dn->dn_dirtyctx != DN_UNDIRTIED) {
for (i = 0; i < TXG_SIZE; i++) {
if (!list_is_empty(&dn->dn_dirty_records[i])) {
clean = B_FALSE;
break;
}
}
}

err = dnode_next_offset(dn, (hole ? DNODE_FIND_HOLE : 0), off, 1, 1, 0);
if (clean)
err = dnode_next_offset(dn,
(hole ? DNODE_FIND_HOLE : 0), off, 1, 1, 0);
else
err = SET_ERROR(EBUSY);

dnode_rele(dn, FTAG);

return (err);
Expand Down
4 changes: 4 additions & 0 deletions module/zfs/zfs_vnops.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ zfs_holey_common(struct inode *ip, int cmd, loff_t *off)
if (error == ESRCH)
return (SET_ERROR(ENXIO));

/* file was dirty, so fall back to using file_sz logic */
if (error == EBUSY)
error = 0;

/*
* We could find a hole that begins after the logical end-of-file,
* because dmu_offset_next() only works on whole blocks. If the
Expand Down

0 comments on commit 99b2905

Please sign in to comment.