zonfs: Fix handling of read-only zones

The write pointer of zones in the read-only consition is defined as
invalid by the SCSI ZBC and ATA ZAC specifications. It is thus not
possible to determine the correct size of a read-only zone file on
mount. Fix this by handling read-only zones in the same manner as
offline zones by disabling all accesses to the zone (read and write)
and initializing the inode size of the read-only zone to 0).

For zones found to be in the read-only condition at runtime, only
disable write access to the zone and keep the size of the zone file to
its last updated value to allow the user to recover previously written
data.

Also fix zonefs documentation file to reflect this change.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
This commit is contained in:
Damien Le Moal 2020-03-20 21:36:54 +09:00
parent 0dda2ddb7d
commit ccf4ad7da0
2 changed files with 34 additions and 12 deletions

View File

@ -258,11 +258,11 @@ conditions.
| option | condition | size read write read write | | option | condition | size read write read write |
+--------------+-----------+-----------------------------------------+ +--------------+-----------+-----------------------------------------+
| | good | fixed yes no yes yes | | | good | fixed yes no yes yes |
| remount-ro | read-only | fixed yes no yes no | | remount-ro | read-only | as is yes no yes no |
| (default) | offline | 0 no no no no | | (default) | offline | 0 no no no no |
+--------------+-----------+-----------------------------------------+ +--------------+-----------+-----------------------------------------+
| | good | fixed yes no yes yes | | | good | fixed yes no yes yes |
| zone-ro | read-only | fixed yes no yes no | | zone-ro | read-only | as is yes no yes no |
| | offline | 0 no no no no | | | offline | 0 no no no no |
+--------------+-----------+-----------------------------------------+ +--------------+-----------+-----------------------------------------+
| | good | 0 no no yes yes | | | good | 0 no no yes yes |
@ -270,7 +270,7 @@ conditions.
| | offline | 0 no no no no | | | offline | 0 no no no no |
+--------------+-----------+-----------------------------------------+ +--------------+-----------+-----------------------------------------+
| | good | fixed yes yes yes yes | | | good | fixed yes yes yes yes |
| repair | read-only | fixed yes no yes no | | repair | read-only | as is yes no yes no |
| | offline | 0 no no no no | | | offline | 0 no no no no |
+--------------+-----------+-----------------------------------------+ +--------------+-----------+-----------------------------------------+
@ -307,8 +307,16 @@ condition changes. The defined behaviors are as follow:
* zone-offline * zone-offline
* repair * repair
The I/O error actions defined for each behavior are detailed in the previous The run-time I/O error actions defined for each behavior are detailed in the
section. previous section. Mount time I/O errors will cause the mount operation to fail.
The handling of read-only zones also differs between mount-time and run-time.
If a read-only zone is found at mount time, the zone is always treated in the
same manner as offline zones, that is, all accesses are disabled and the zone
file size set to 0. This is necessary as the write pointer of read-only zones
is defined as invalib by the ZBC and ZAC standards, making it impossible to
discover the amount of data that has been written to the zone. In the case of a
read-only zone discovered at run-time, as indicated in the previous section.
the size of the zone file is left unchanged from its last updated value.
Zonefs User Space Tools Zonefs User Space Tools
======================= =======================

View File

@ -178,7 +178,8 @@ static void zonefs_update_stats(struct inode *inode, loff_t new_isize)
* amount of readable data in the zone. * amount of readable data in the zone.
*/ */
static loff_t zonefs_check_zone_condition(struct inode *inode, static loff_t zonefs_check_zone_condition(struct inode *inode,
struct blk_zone *zone, bool warn) struct blk_zone *zone, bool warn,
bool mount)
{ {
struct zonefs_inode_info *zi = ZONEFS_I(inode); struct zonefs_inode_info *zi = ZONEFS_I(inode);
@ -196,13 +197,26 @@ static loff_t zonefs_check_zone_condition(struct inode *inode,
zone->wp = zone->start; zone->wp = zone->start;
return 0; return 0;
case BLK_ZONE_COND_READONLY: case BLK_ZONE_COND_READONLY:
/* Do not allow writes in read-only zones */ /*
* The write pointer of read-only zones is invalid. If such a
* zone is found during mount, the file size cannot be retrieved
* so we treat the zone as offline (mount == true case).
* Otherwise, keep the file size as it was when last updated
* so that the user can recover data. In both cases, writes are
* always disabled for the zone.
*/
if (warn) if (warn)
zonefs_warn(inode->i_sb, "inode %lu: read-only zone\n", zonefs_warn(inode->i_sb, "inode %lu: read-only zone\n",
inode->i_ino); inode->i_ino);
inode->i_flags |= S_IMMUTABLE; inode->i_flags |= S_IMMUTABLE;
if (mount) {
zone->cond = BLK_ZONE_COND_OFFLINE;
inode->i_mode &= ~0777;
zone->wp = zone->start;
return 0;
}
inode->i_mode &= ~0222; inode->i_mode &= ~0222;
/* fallthrough */ return i_size_read(inode);
default: default:
if (zi->i_ztype == ZONEFS_ZTYPE_CNV) if (zi->i_ztype == ZONEFS_ZTYPE_CNV)
return zi->i_max_size; return zi->i_max_size;
@ -231,7 +245,7 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
* as there is no inconsistency between the inode size and the amount of * as there is no inconsistency between the inode size and the amount of
* data writen in the zone (data_size). * data writen in the zone (data_size).
*/ */
data_size = zonefs_check_zone_condition(inode, zone, true); data_size = zonefs_check_zone_condition(inode, zone, true, false);
isize = i_size_read(inode); isize = i_size_read(inode);
if (zone->cond != BLK_ZONE_COND_OFFLINE && if (zone->cond != BLK_ZONE_COND_OFFLINE &&
zone->cond != BLK_ZONE_COND_READONLY && zone->cond != BLK_ZONE_COND_READONLY &&
@ -274,7 +288,7 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
if (zone->cond != BLK_ZONE_COND_OFFLINE) { if (zone->cond != BLK_ZONE_COND_OFFLINE) {
zone->cond = BLK_ZONE_COND_OFFLINE; zone->cond = BLK_ZONE_COND_OFFLINE;
data_size = zonefs_check_zone_condition(inode, zone, data_size = zonefs_check_zone_condition(inode, zone,
false); false, false);
} }
} else if (zone->cond == BLK_ZONE_COND_READONLY || } else if (zone->cond == BLK_ZONE_COND_READONLY ||
sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) { sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) {
@ -283,7 +297,7 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
if (zone->cond != BLK_ZONE_COND_READONLY) { if (zone->cond != BLK_ZONE_COND_READONLY) {
zone->cond = BLK_ZONE_COND_READONLY; zone->cond = BLK_ZONE_COND_READONLY;
data_size = zonefs_check_zone_condition(inode, zone, data_size = zonefs_check_zone_condition(inode, zone,
false); false, false);
} }
} }
@ -975,7 +989,7 @@ static void zonefs_init_file_inode(struct inode *inode, struct blk_zone *zone,
zi->i_zsector = zone->start; zi->i_zsector = zone->start;
zi->i_max_size = min_t(loff_t, MAX_LFS_FILESIZE, zi->i_max_size = min_t(loff_t, MAX_LFS_FILESIZE,
zone->len << SECTOR_SHIFT); zone->len << SECTOR_SHIFT);
zi->i_wpoffset = zonefs_check_zone_condition(inode, zone, true); zi->i_wpoffset = zonefs_check_zone_condition(inode, zone, true, true);
inode->i_uid = sbi->s_uid; inode->i_uid = sbi->s_uid;
inode->i_gid = sbi->s_gid; inode->i_gid = sbi->s_gid;