Jiri Slaby ba7816
From: Damien Le Moal <damien.lemoal@opensource.wdc.com>
Jiri Slaby ba7816
Date: Fri, 25 Nov 2022 11:06:20 +0900
Jiri Slaby ba7816
Subject: [PATCH] zonefs: Simplify IO error handling
Jiri Slaby ba7816
References: bsc#1012628
Jiri Slaby ba7816
Patch-mainline: 6.2.10
Jiri Slaby ba7816
Git-commit: 46a9c526eef7fb68a00321e2a9591ce5276ae92b
Jiri Slaby ba7816
Jiri Slaby ba7816
[ Upstream commit 46a9c526eef7fb68a00321e2a9591ce5276ae92b ]
Jiri Slaby ba7816
Jiri Slaby ba7816
Simplify zonefs_check_zone_condition() by moving the code that changes
Jiri Slaby ba7816
an inode access rights to the new function zonefs_inode_update_mode().
Jiri Slaby ba7816
Furthermore, since on mount an inode wpoffset is always zero when
Jiri Slaby ba7816
zonefs_check_zone_condition() is called during an inode initialization,
Jiri Slaby ba7816
the "mount" boolean argument is not necessary for the readonly zone
Jiri Slaby ba7816
case. This argument is thus removed.
Jiri Slaby ba7816
Jiri Slaby ba7816
zonefs_io_error_cb() is also modified to use the inode offline and
Jiri Slaby ba7816
zone state flags instead of checking the device zone condition. The
Jiri Slaby ba7816
multiple calls to zonefs_check_zone_condition() are reduced to the first
Jiri Slaby ba7816
call on entry, which allows removing the "warn" argument.
Jiri Slaby ba7816
zonefs_inode_update_mode() is also used to update an inode access rights
Jiri Slaby ba7816
as zonefs_io_error_cb() modifies the inode flags depending on the volume
Jiri Slaby ba7816
error handling mode (defined with a mount option). Since an inode mode
Jiri Slaby ba7816
change differs for read-only zones between mount time and IO error time,
Jiri Slaby ba7816
the flag ZONEFS_ZONE_INIT_MODE is used to differentiate both cases.
Jiri Slaby ba7816
Jiri Slaby ba7816
Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
Jiri Slaby ba7816
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Jiri Slaby ba7816
Stable-dep-of: 88b170088ad2 ("zonefs: Fix error message in zonefs_file_dio_append()")
Jiri Slaby ba7816
Signed-off-by: Sasha Levin <sashal@kernel.org>
Jiri Slaby ba7816
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Jiri Slaby ba7816
---
Jiri Slaby ba7816
 fs/zonefs/super.c  | 110 ++++++++++++++++++++++++---------------------
Jiri Slaby ba7816
 fs/zonefs/zonefs.h |   9 ++--
Jiri Slaby ba7816
 2 files changed, 64 insertions(+), 55 deletions(-)
Jiri Slaby ba7816
Jiri Slaby ba7816
diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c
Jiri Slaby ba7816
index e808276b..6307cc95 100644
Jiri Slaby ba7816
--- a/fs/zonefs/super.c
Jiri Slaby ba7816
+++ b/fs/zonefs/super.c
Jiri Slaby ba7816
@@ -155,48 +155,31 @@ void zonefs_update_stats(struct inode *inode, loff_t new_isize)
Jiri Slaby ba7816
  * amount of readable data in the zone.
Jiri Slaby ba7816
  */
Jiri Slaby ba7816
 static loff_t zonefs_check_zone_condition(struct inode *inode,
Jiri Slaby ba7816
-					  struct blk_zone *zone, bool warn,
Jiri Slaby ba7816
-					  bool mount)
Jiri Slaby ba7816
+					  struct blk_zone *zone)
Jiri Slaby ba7816
 {
Jiri Slaby ba7816
 	struct zonefs_inode_info *zi = ZONEFS_I(inode);
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	switch (zone->cond) {
Jiri Slaby ba7816
 	case BLK_ZONE_COND_OFFLINE:
Jiri Slaby ba7816
-		/*
Jiri Slaby ba7816
-		 * Dead zone: make the inode immutable, disable all accesses
Jiri Slaby ba7816
-		 * and set the file size to 0 (zone wp set to zone start).
Jiri Slaby ba7816
-		 */
Jiri Slaby ba7816
-		if (warn)
Jiri Slaby ba7816
-			zonefs_warn(inode->i_sb, "inode %lu: offline zone\n",
Jiri Slaby ba7816
-				    inode->i_ino);
Jiri Slaby ba7816
-		inode->i_flags |= S_IMMUTABLE;
Jiri Slaby ba7816
-		inode->i_mode &= ~0777;
Jiri Slaby ba7816
-		zone->wp = zone->start;
Jiri Slaby ba7816
+		zonefs_warn(inode->i_sb, "inode %lu: offline zone\n",
Jiri Slaby ba7816
+			    inode->i_ino);
Jiri Slaby ba7816
 		zi->i_flags |= ZONEFS_ZONE_OFFLINE;
Jiri Slaby ba7816
 		return 0;
Jiri Slaby ba7816
 	case BLK_ZONE_COND_READONLY:
Jiri Slaby ba7816
 		/*
Jiri Slaby ba7816
-		 * The write pointer of read-only zones is invalid. If such a
Jiri Slaby ba7816
-		 * zone is found during mount, the file size cannot be retrieved
Jiri Slaby ba7816
-		 * so we treat the zone as offline (mount == true case).
Jiri Slaby ba7816
-		 * Otherwise, keep the file size as it was when last updated
Jiri Slaby ba7816
-		 * so that the user can recover data. In both cases, writes are
Jiri Slaby ba7816
-		 * always disabled for the zone.
Jiri Slaby ba7816
+		 * The write pointer of read-only zones is invalid, so we cannot
Jiri Slaby ba7816
+		 * determine the zone wpoffset (inode size). We thus keep the
Jiri Slaby ba7816
+		 * zone wpoffset as is, which leads to an empty file
Jiri Slaby ba7816
+		 * (wpoffset == 0) on mount. For a runtime error, this keeps
Jiri Slaby ba7816
+		 * the inode size as it was when last updated so that the user
Jiri Slaby ba7816
+		 * can recover data.
Jiri Slaby ba7816
 		 */
Jiri Slaby ba7816
-		if (warn)
Jiri Slaby ba7816
-			zonefs_warn(inode->i_sb, "inode %lu: read-only zone\n",
Jiri Slaby ba7816
-				    inode->i_ino);
Jiri Slaby ba7816
-		inode->i_flags |= S_IMMUTABLE;
Jiri Slaby ba7816
-		if (mount) {
Jiri Slaby ba7816
-			zone->cond = BLK_ZONE_COND_OFFLINE;
Jiri Slaby ba7816
-			inode->i_mode &= ~0777;
Jiri Slaby ba7816
-			zone->wp = zone->start;
Jiri Slaby ba7816
-			zi->i_flags |= ZONEFS_ZONE_OFFLINE;
Jiri Slaby ba7816
-			return 0;
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
+		zonefs_warn(inode->i_sb, "inode %lu: read-only zone\n",
Jiri Slaby ba7816
+			    inode->i_ino);
Jiri Slaby ba7816
 		zi->i_flags |= ZONEFS_ZONE_READONLY;
Jiri Slaby ba7816
-		inode->i_mode &= ~0222;
Jiri Slaby ba7816
-		return i_size_read(inode);
Jiri Slaby ba7816
+		if (zi->i_ztype == ZONEFS_ZTYPE_CNV)
Jiri Slaby ba7816
+			return zi->i_max_size;
Jiri Slaby ba7816
+		return zi->i_wpoffset;
Jiri Slaby ba7816
 	case BLK_ZONE_COND_FULL:
Jiri Slaby ba7816
 		/* The write pointer of full zones is invalid. */
Jiri Slaby ba7816
 		return zi->i_max_size;
Jiri Slaby ba7816
@@ -207,6 +190,30 @@ static loff_t zonefs_check_zone_condition(struct inode *inode,
Jiri Slaby ba7816
 	}
Jiri Slaby ba7816
 }
Jiri Slaby ba7816
 
Jiri Slaby ba7816
+/*
Jiri Slaby ba7816
+ * Check a zone condition and adjust its inode access permissions for
Jiri Slaby ba7816
+ * offline and readonly zones.
Jiri Slaby ba7816
+ */
Jiri Slaby ba7816
+static void zonefs_inode_update_mode(struct inode *inode)
Jiri Slaby ba7816
+{
Jiri Slaby ba7816
+	struct zonefs_inode_info *zi = ZONEFS_I(inode);
Jiri Slaby ba7816
+
Jiri Slaby ba7816
+	if (zi->i_flags & ZONEFS_ZONE_OFFLINE) {
Jiri Slaby ba7816
+		/* Offline zones cannot be read nor written */
Jiri Slaby ba7816
+		inode->i_flags |= S_IMMUTABLE;
Jiri Slaby ba7816
+		inode->i_mode &= ~0777;
Jiri Slaby ba7816
+	} else if (zi->i_flags & ZONEFS_ZONE_READONLY) {
Jiri Slaby ba7816
+		/* Readonly zones cannot be written */
Jiri Slaby ba7816
+		inode->i_flags |= S_IMMUTABLE;
Jiri Slaby ba7816
+		if (zi->i_flags & ZONEFS_ZONE_INIT_MODE)
Jiri Slaby ba7816
+			inode->i_mode &= ~0777;
Jiri Slaby ba7816
+		else
Jiri Slaby ba7816
+			inode->i_mode &= ~0222;
Jiri Slaby ba7816
+	}
Jiri Slaby ba7816
+
Jiri Slaby ba7816
+	zi->i_flags &= ~ZONEFS_ZONE_INIT_MODE;
Jiri Slaby ba7816
+}
Jiri Slaby ba7816
+
Jiri Slaby ba7816
 struct zonefs_ioerr_data {
Jiri Slaby ba7816
 	struct inode	*inode;
Jiri Slaby ba7816
 	bool		write;
Jiri Slaby ba7816
@@ -228,10 +235,9 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
Jiri Slaby ba7816
 	 * as there is no inconsistency between the inode size and the amount of
Jiri Slaby ba7816
 	 * data writen in the zone (data_size).
Jiri Slaby ba7816
 	 */
Jiri Slaby ba7816
-	data_size = zonefs_check_zone_condition(inode, zone, true, false);
Jiri Slaby ba7816
+	data_size = zonefs_check_zone_condition(inode, zone);
Jiri Slaby ba7816
 	isize = i_size_read(inode);
Jiri Slaby ba7816
-	if (zone->cond != BLK_ZONE_COND_OFFLINE &&
Jiri Slaby ba7816
-	    zone->cond != BLK_ZONE_COND_READONLY &&
Jiri Slaby ba7816
+	if (!(zi->i_flags & (ZONEFS_ZONE_READONLY | ZONEFS_ZONE_OFFLINE)) &&
Jiri Slaby ba7816
 	    !err->write && isize == data_size)
Jiri Slaby ba7816
 		return 0;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
@@ -264,24 +270,22 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
Jiri Slaby ba7816
 	 * zone condition to read-only and offline respectively, as if the
Jiri Slaby ba7816
 	 * condition was signaled by the hardware.
Jiri Slaby ba7816
 	 */
Jiri Slaby ba7816
-	if (zone->cond == BLK_ZONE_COND_OFFLINE ||
Jiri Slaby ba7816
-	    sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL) {
Jiri Slaby ba7816
+	if ((zi->i_flags & ZONEFS_ZONE_OFFLINE) ||
Jiri Slaby ba7816
+	    (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL)) {
Jiri Slaby ba7816
 		zonefs_warn(sb, "inode %lu: read/write access disabled\n",
Jiri Slaby ba7816
 			    inode->i_ino);
Jiri Slaby ba7816
-		if (zone->cond != BLK_ZONE_COND_OFFLINE) {
Jiri Slaby ba7816
-			zone->cond = BLK_ZONE_COND_OFFLINE;
Jiri Slaby ba7816
-			data_size = zonefs_check_zone_condition(inode, zone,
Jiri Slaby ba7816
-								false, false);
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
-	} else if (zone->cond == BLK_ZONE_COND_READONLY ||
Jiri Slaby ba7816
-		   sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) {
Jiri Slaby ba7816
+		if (!(zi->i_flags & ZONEFS_ZONE_OFFLINE))
Jiri Slaby ba7816
+			zi->i_flags |= ZONEFS_ZONE_OFFLINE;
Jiri Slaby ba7816
+		zonefs_inode_update_mode(inode);
Jiri Slaby ba7816
+		data_size = 0;
Jiri Slaby ba7816
+	} else if ((zi->i_flags & ZONEFS_ZONE_READONLY) ||
Jiri Slaby ba7816
+		   (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO)) {
Jiri Slaby ba7816
 		zonefs_warn(sb, "inode %lu: write access disabled\n",
Jiri Slaby ba7816
 			    inode->i_ino);
Jiri Slaby ba7816
-		if (zone->cond != BLK_ZONE_COND_READONLY) {
Jiri Slaby ba7816
-			zone->cond = BLK_ZONE_COND_READONLY;
Jiri Slaby ba7816
-			data_size = zonefs_check_zone_condition(inode, zone,
Jiri Slaby ba7816
-								false, false);
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
+		if (!(zi->i_flags & ZONEFS_ZONE_READONLY))
Jiri Slaby ba7816
+			zi->i_flags |= ZONEFS_ZONE_READONLY;
Jiri Slaby ba7816
+		zonefs_inode_update_mode(inode);
Jiri Slaby ba7816
+		data_size = isize;
Jiri Slaby ba7816
 	} else if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO &&
Jiri Slaby ba7816
 		   data_size > isize) {
Jiri Slaby ba7816
 		/* Do not expose garbage data */
Jiri Slaby ba7816
@@ -295,8 +299,7 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
Jiri Slaby ba7816
 	 * close of the zone when the inode file is closed.
Jiri Slaby ba7816
 	 */
Jiri Slaby ba7816
 	if ((sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) &&
Jiri Slaby ba7816
-	    (zone->cond == BLK_ZONE_COND_OFFLINE ||
Jiri Slaby ba7816
-	     zone->cond == BLK_ZONE_COND_READONLY))
Jiri Slaby ba7816
+	    (zi->i_flags & (ZONEFS_ZONE_READONLY | ZONEFS_ZONE_OFFLINE)))
Jiri Slaby ba7816
 		zi->i_flags &= ~ZONEFS_ZONE_OPEN;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	/*
Jiri Slaby ba7816
@@ -378,6 +381,7 @@ static struct inode *zonefs_alloc_inode(struct super_block *sb)
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	inode_init_once(&zi->i_vnode);
Jiri Slaby ba7816
 	mutex_init(&zi->i_truncate_mutex);
Jiri Slaby ba7816
+	zi->i_wpoffset = 0;
Jiri Slaby ba7816
 	zi->i_wr_refcnt = 0;
Jiri Slaby ba7816
 	zi->i_flags = 0;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
@@ -594,7 +598,7 @@ static int zonefs_init_file_inode(struct inode *inode, struct blk_zone *zone,
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	zi->i_max_size = min_t(loff_t, MAX_LFS_FILESIZE,
Jiri Slaby ba7816
 			       zone->capacity << SECTOR_SHIFT);
Jiri Slaby ba7816
-	zi->i_wpoffset = zonefs_check_zone_condition(inode, zone, true, true);
Jiri Slaby ba7816
+	zi->i_wpoffset = zonefs_check_zone_condition(inode, zone);
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	inode->i_uid = sbi->s_uid;
Jiri Slaby ba7816
 	inode->i_gid = sbi->s_gid;
Jiri Slaby ba7816
@@ -605,6 +609,10 @@ static int zonefs_init_file_inode(struct inode *inode, struct blk_zone *zone,
Jiri Slaby ba7816
 	inode->i_fop = &zonefs_file_operations;
Jiri Slaby ba7816
 	inode->i_mapping->a_ops = &zonefs_file_aops;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
+	/* Update the inode access rights depending on the zone condition */
Jiri Slaby ba7816
+	zi->i_flags |= ZONEFS_ZONE_INIT_MODE;
Jiri Slaby ba7816
+	zonefs_inode_update_mode(inode);
Jiri Slaby ba7816
+
Jiri Slaby ba7816
 	sb->s_maxbytes = max(zi->i_max_size, sb->s_maxbytes);
Jiri Slaby ba7816
 	sbi->s_blocks += zi->i_max_size >> sb->s_blocksize_bits;
Jiri Slaby ba7816
 	sbi->s_used_blocks += zi->i_wpoffset >> sb->s_blocksize_bits;
Jiri Slaby ba7816
diff --git a/fs/zonefs/zonefs.h b/fs/zonefs/zonefs.h
Jiri Slaby ba7816
index 839ebe9a..43909644 100644
Jiri Slaby ba7816
--- a/fs/zonefs/zonefs.h
Jiri Slaby ba7816
+++ b/fs/zonefs/zonefs.h
Jiri Slaby ba7816
@@ -39,10 +39,11 @@ static inline enum zonefs_ztype zonefs_zone_type(struct blk_zone *zone)
Jiri Slaby ba7816
 	return ZONEFS_ZTYPE_SEQ;
Jiri Slaby ba7816
 }
Jiri Slaby ba7816
 
Jiri Slaby ba7816
-#define ZONEFS_ZONE_OPEN	(1U << 0)
Jiri Slaby ba7816
-#define ZONEFS_ZONE_ACTIVE	(1U << 1)
Jiri Slaby ba7816
-#define ZONEFS_ZONE_OFFLINE	(1U << 2)
Jiri Slaby ba7816
-#define ZONEFS_ZONE_READONLY	(1U << 3)
Jiri Slaby ba7816
+#define ZONEFS_ZONE_INIT_MODE	(1U << 0)
Jiri Slaby ba7816
+#define ZONEFS_ZONE_OPEN	(1U << 1)
Jiri Slaby ba7816
+#define ZONEFS_ZONE_ACTIVE	(1U << 2)
Jiri Slaby ba7816
+#define ZONEFS_ZONE_OFFLINE	(1U << 3)
Jiri Slaby ba7816
+#define ZONEFS_ZONE_READONLY	(1U << 4)
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 /*
Jiri Slaby ba7816
  * In-memory inode data.
Jiri Slaby ba7816
-- 
Jiri Slaby ba7816
2.35.3
Jiri Slaby ba7816