Commit 7dd12d65 authored by Damien Le Moal's avatar Damien Le Moal

zonefs: fix zone report size in __zonefs_io_error()

When an IO error occurs, the function __zonefs_io_error() is used to
issue a zone report to obtain the latest zone information from the
device. This function gets a zone report for all zones used as storage
for a file, which is always 1 zone except for files representing
aggregated conventional zones.

The number of zones of a zone report for a file is calculated in
__zonefs_io_error() by doing a bit-shift of the inode i_zone_size field,
which is equal to or larger than the device zone size. However, this
calculation does not take into account that the last zone of a zoned
device may be smaller than the zone size reported by bdev_zone_sectors()
(which is used to set the bit shift size). As a result, if an error
occurs for an IO targetting such last smaller zone, the zone report will
ask for 0 zones, leading to an invalid zone report.

Fix this by using the fact that all files require a 1 zone report,
except if the inode i_zone_size field indicates a zone size larger than
the device zone size. This exception case corresponds to a mount with
aggregated conventional zones.

A check for this exception is added to the file inode initialization
during mount. If an invalid setup is detected, emit an error and fail
the mount (check contributed by Johannes Thumshirn).
Signed-off-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: default avatarDamien Le Moal <damien.lemoal@opensource.wdc.com>
parent 094226ad
...@@ -478,14 +478,22 @@ static void __zonefs_io_error(struct inode *inode, bool write) ...@@ -478,14 +478,22 @@ static void __zonefs_io_error(struct inode *inode, bool write)
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct zonefs_sb_info *sbi = ZONEFS_SB(sb); struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
unsigned int noio_flag; unsigned int noio_flag;
unsigned int nr_zones = unsigned int nr_zones = 1;
zi->i_zone_size >> (sbi->s_zone_sectors_shift + SECTOR_SHIFT);
struct zonefs_ioerr_data err = { struct zonefs_ioerr_data err = {
.inode = inode, .inode = inode,
.write = write, .write = write,
}; };
int ret; int ret;
/*
* The only files that have more than one zone are conventional zone
* files with aggregated conventional zones, for which the inode zone
* size is always larger than the device zone size.
*/
if (zi->i_zone_size > bdev_zone_sectors(sb->s_bdev))
nr_zones = zi->i_zone_size >>
(sbi->s_zone_sectors_shift + SECTOR_SHIFT);
/* /*
* Memory allocations in blkdev_report_zones() can trigger a memory * Memory allocations in blkdev_report_zones() can trigger a memory
* reclaim which may in turn cause a recursion into zonefs as well as * reclaim which may in turn cause a recursion into zonefs as well as
...@@ -1407,6 +1415,14 @@ static int zonefs_init_file_inode(struct inode *inode, struct blk_zone *zone, ...@@ -1407,6 +1415,14 @@ static int zonefs_init_file_inode(struct inode *inode, struct blk_zone *zone,
zi->i_ztype = type; zi->i_ztype = type;
zi->i_zsector = zone->start; zi->i_zsector = zone->start;
zi->i_zone_size = zone->len << SECTOR_SHIFT; zi->i_zone_size = zone->len << SECTOR_SHIFT;
if (zi->i_zone_size > bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT &&
!(sbi->s_features & ZONEFS_F_AGGRCNV)) {
zonefs_err(sb,
"zone size %llu doesn't match device's zone sectors %llu\n",
zi->i_zone_size,
bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT);
return -EINVAL;
}
zi->i_max_size = min_t(loff_t, MAX_LFS_FILESIZE, zi->i_max_size = min_t(loff_t, MAX_LFS_FILESIZE,
zone->capacity << SECTOR_SHIFT); zone->capacity << SECTOR_SHIFT);
...@@ -1456,11 +1472,11 @@ static struct dentry *zonefs_create_inode(struct dentry *parent, ...@@ -1456,11 +1472,11 @@ static struct dentry *zonefs_create_inode(struct dentry *parent,
struct inode *dir = d_inode(parent); struct inode *dir = d_inode(parent);
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
int ret; int ret = -ENOMEM;
dentry = d_alloc_name(parent, name); dentry = d_alloc_name(parent, name);
if (!dentry) if (!dentry)
return NULL; return ERR_PTR(ret);
inode = new_inode(parent->d_sb); inode = new_inode(parent->d_sb);
if (!inode) if (!inode)
...@@ -1485,7 +1501,7 @@ static struct dentry *zonefs_create_inode(struct dentry *parent, ...@@ -1485,7 +1501,7 @@ static struct dentry *zonefs_create_inode(struct dentry *parent,
dput: dput:
dput(dentry); dput(dentry);
return NULL; return ERR_PTR(ret);
} }
struct zonefs_zone_data { struct zonefs_zone_data {
...@@ -1505,7 +1521,7 @@ static int zonefs_create_zgroup(struct zonefs_zone_data *zd, ...@@ -1505,7 +1521,7 @@ static int zonefs_create_zgroup(struct zonefs_zone_data *zd,
struct blk_zone *zone, *next, *end; struct blk_zone *zone, *next, *end;
const char *zgroup_name; const char *zgroup_name;
char *file_name; char *file_name;
struct dentry *dir; struct dentry *dir, *dent;
unsigned int n = 0; unsigned int n = 0;
int ret; int ret;
...@@ -1523,8 +1539,8 @@ static int zonefs_create_zgroup(struct zonefs_zone_data *zd, ...@@ -1523,8 +1539,8 @@ static int zonefs_create_zgroup(struct zonefs_zone_data *zd,
zgroup_name = "seq"; zgroup_name = "seq";
dir = zonefs_create_inode(sb->s_root, zgroup_name, NULL, type); dir = zonefs_create_inode(sb->s_root, zgroup_name, NULL, type);
if (!dir) { if (IS_ERR(dir)) {
ret = -ENOMEM; ret = PTR_ERR(dir);
goto free; goto free;
} }
...@@ -1570,8 +1586,9 @@ static int zonefs_create_zgroup(struct zonefs_zone_data *zd, ...@@ -1570,8 +1586,9 @@ static int zonefs_create_zgroup(struct zonefs_zone_data *zd,
* Use the file number within its group as file name. * Use the file number within its group as file name.
*/ */
snprintf(file_name, ZONEFS_NAME_MAX - 1, "%u", n); snprintf(file_name, ZONEFS_NAME_MAX - 1, "%u", n);
if (!zonefs_create_inode(dir, file_name, zone, type)) { dent = zonefs_create_inode(dir, file_name, zone, type);
ret = -ENOMEM; if (IS_ERR(dent)) {
ret = PTR_ERR(dent);
goto free; goto free;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment