Skip to contentSkip to conversation

ext4: force inode writes when nfsd calls commit_metadata()

PATCH New Theodore Ts'o 1 participant 1 message Dec 19, 2018

Subsystems (2)
lore
Share & tools
lore

Theodore Ts'o

author maintainer
#0 lore raw

PATCH 1/1 ext4: force inode writes when nfsd calls commit_metadata()

Some time back, nfsd switched from calling vfs_fsync() to using a new commit_metadata() hook in export_operations(). If the file system did not provide a commit_metadata() hook, it fell back to using sync_inode_metadata(). Unfortunately doesn't work on all file systems. In particular, it doesn't work on ext4 due to how the inode gets journalled --- the VFS writeback code never ends up calling ext4_write_inode().

So we need to provide our own ext4_nfs_commit_metdata() method which calls ext4_write_inode() directly.

Google-Bug-Id: 121195940

---
 fs/ext4/super.c             | 12 ++++++++++++
 include/trace/events/ext4.h | 20 ++++++++++++++++++++
 2 files changed, 32 insertions(+)
fs/ext4/super.c +12 -0
index fc9071081600..378977cf8c19 100644
@@ -1202,6 +1202,17 @@ static struct dentry *ext4_fh_to_parent(struct super_block *sb, struct fid *fid,
 				    ext4_nfs_get_inode);
 }
 
+static int ext4_nfs_commit_metadata(struct inode *inode)
+{
+	struct writeback_control wbc = {
+		.sync_mode = WB_SYNC_ALL
+	};
+
+	trace_ext4_nfs_commit_metadata(inode);
+	ext4_write_inode(inode, &wbc);
+	return 0;
+}
+
 /*
  * Try to release metadata pages (indirect blocks, directories) which are
  * mapped via the block device.  Since these pages could have journal heads
@@ -1406,6 +1417,7 @@ static const struct export_operations ext4_export_ops = {
 	.fh_to_dentry = ext4_fh_to_dentry,
 	.fh_to_parent = ext4_fh_to_parent,
 	.get_parent = ext4_get_parent,
+	.commit_metadata = ext4_nfs_commit_metadata,
 };
 
 enum {
include/trace/events/ext4.h +20 -0
index 698e0d8a5ca4..d68e9e536814 100644
@@ -226,6 +226,26 @@ TRACE_EVENT(ext4_drop_inode,
 		  (unsigned long) __entry->ino, __entry->drop)
 );
 
+TRACE_EVENT(ext4_nfs_commit_metadata,
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+	),
+
+	TP_printk("dev %d,%d ino %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino)
+);
+
 TRACE_EVENT(ext4_mark_inode_dirty,
 	TP_PROTO(struct inode *inode, unsigned long IP),
 
Signature · 1 line
2.19.1