git: 7769487a50ff - releng/12.4 - dnode_is_dirty: check dnode and its data for dirtiness

From: Gordon Tetlow <gordon_at_FreeBSD.org>
Date: Fri, 01 Dec 2023 00:38:55 UTC
The branch releng/12.4 has been updated by gordon:

URL: https://cgit.FreeBSD.org/src/commit/?id=7769487a50ff81a31c313538d914f0dc8deb5ea1

commit 7769487a50ff81a31c313538d914f0dc8deb5ea1
Author:     Rob Norris <rob.norris@klarasystems.com>
AuthorDate: 2023-11-30 05:14:13 +0000
Commit:     Gordon Tetlow <gordon@FreeBSD.org>
CommitDate: 2023-11-30 21:46:13 +0000

    dnode_is_dirty: check dnode and its data for dirtiness
    
    Over its history this the dirty dnode test has been changed between
    checking for a dnodes being on `os_dirty_dnodes` (`dn_dirty_link`) and
    `dn_dirty_record`.
    
    It turns out both are actually required.
    
    In the case of appending data to a newly created file, the dnode proper
    is dirtied (at least to change the blocksize) and dirty records are
    added.  Thus, a single logical operation is represented by separate
    dirty indicators, and must not be separated.
    
    The incorrect dirty check becomes a problem when the first block of a
    file is being appended to while another process is calling lseek to skip
    holes. There is a small window where the dnode part is undirtied while
    there are still dirty records. In this case, `lseek(fd, 0, SEEK_DATA)`
    would not know that the file is dirty, and would go to
    `dnode_next_offset()`. Since the object has no data blocks yet, it
    returns `ESRCH`, indicating no data found, which results in `ENXIO`
    being returned to `lseek()`'s caller.
    
    This change simply updates the dirty check to check both types of dirty.
    If there's anything dirty at all, we immediately go to the "wait for
    sync" stage, It doesn't really matter after that; both changes are on
    disk, so the dirty fields should be correct.
    
    Sponsored by:   Klara, Inc.
    Sponsored by:   Wasabi Technology, Inc.
    Approved by:    so
    Security:       FreeBSD-EN-23:16.openzfs
    
    (cherry picked from commit 8959056352256e79a63ba6327f0a0ee4236d3e7f)
---
 sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c
index 45ac4b796fd2..bd78daf5d517 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c
@@ -2557,7 +2557,8 @@ dmu_object_wait_synced(objset_t *os, uint64_t object)
 	}
 
 	for (i = 0; i < TXG_SIZE; i++) {
-		if (list_link_active(&dn->dn_dirty_link[i])) {
+		if (list_link_active(&dn->dn_dirty_link[i]) ||
+		    !list_is_empty(&dn->dn_dirty_records[i])) {
 			break;
 		}
 	}