31
31
#include <sys/zfs_ctldir.h>
32
32
#include <sys/zpl.h>
33
33
#include <linux/iversion.h>
34
+ #include <linux/version.h>
34
35
35
36
36
37
static struct inode *
@@ -105,6 +106,42 @@ zpl_put_super(struct super_block *sb)
105
106
ASSERT3S (error , <=, 0 );
106
107
}
107
108
109
+ /*
110
+ * zfs_sync() is the underlying implementation for the sync(2) and syncfs(2)
111
+ * syscalls, via sb->s_op->sync_fs().
112
+ *
113
+ * Before kernel 5.17 (torvalds/linux@5679897eb104), syncfs() ->
114
+ * sync_filesystem() would ignore the return from instead only considing the
115
+ * error from syncing the underlying block device (sb->s_dev). Since OpenZFS
116
+ * doesn't _have_ an underlying block device, there's no way for us to report a
117
+ * sync directly.
118
+ *
119
+ * However, in 5.8 (torvalds/linux@735e4ae5ba28) the superblock gained an extra
120
+ * error store `s_wb_err`, to carry errors seen on page writeback since the
121
+ * last call to syncfs(). If sync_filesystem() does not return an error, any
122
+ * existing writeback error on the superblock will be used instead (and cleared
123
+ * either way). We don't use this (page writeback is a different thing for us),
124
+ * so for 5.8-5.17 we can use that instead to get syncfs() to return the error.
125
+ *
126
+ * Before 5.8, we have no other good options - no matter what happens, the
127
+ * userspace program will be told the call has succeeded, and so we must make
128
+ * it so, Therefore, when we are asked to wait for sync to complete (wait ==
129
+ * 1), if zfs_sync() has returned an error we have no choice but to block,
130
+ * regardless of the reason.
131
+ *
132
+ * The 5.17 change was backported to the 5.10, 5.15 and 5.16 series, and likely
133
+ * to some vendor kernels. Meanwhile, s_wb_err is still in use in 6.15 (the
134
+ * mainline Linux series at time of writing), and has likely been backported to
135
+ * vendor kernels before 5.8. We don't really want to use a workaround when we
136
+ * don't have to, but we can't really detect whether or not sync_filesystem()
137
+ * will return our errors (without a difficult runtime test anyway). So, we use
138
+ * a static version check: any kernel reporting its version as 5.17+ will use a
139
+ * direct error return, otherwise, we'll either use s_wb_err if it was detected
140
+ * at configure (5.8-5.16 + vendor backports). If it's unavailable, we will
141
+ * block to ensure the correct semantics.
142
+ *
143
+ * See https://github.com/openzfs/zfs/issues/17416 for further discussion.
144
+ */
108
145
static int
109
146
zpl_sync_fs (struct super_block * sb , int wait )
110
147
{
@@ -115,10 +152,28 @@ zpl_sync_fs(struct super_block *sb, int wait)
115
152
crhold (cr );
116
153
cookie = spl_fstrans_mark ();
117
154
error = - zfs_sync (sb , wait , cr );
155
+
156
+ #if LINUX_VERSION_CODE < KERNEL_VERSION (5 , 17 , 0 )
157
+ #ifdef HAVE_SUPER_BLOCK_S_WB_ERR
158
+ if (error && wait )
159
+ errseq_set (& sb -> s_wb_err , error );
160
+ #else
161
+ if (error && wait ) {
162
+ zfsvfs_t * zfsvfs = sb -> s_fs_info ;
163
+ ASSERT3P (zfsvfs , != , NULL );
164
+ if (zfs_enter (zfsvfs , FTAG ) == 0 ) {
165
+ txg_wait_synced (dmu_objset_pool (zfsvfs -> z_os ), 0 );
166
+ zfs_exit (zfsvfs , FTAG );
167
+ error = 0 ;
168
+ }
169
+ }
170
+ #endif
171
+ #endif /* < 5.17.0 */
172
+
118
173
spl_fstrans_unmark (cookie );
119
174
crfree (cr );
120
- ASSERT3S (error , <=, 0 );
121
175
176
+ ASSERT3S (error , <=, 0 );
122
177
return (error );
123
178
}
124
179
0 commit comments