// SPDX-License-Identifier: GPL-2.0
/*
* Simple file system for zoned block devices exposing zones as files.
*
* Copyright (C) 2019 Western Digital Corporation or its affiliates.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/magic.h>
#include <linux/iomap.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/statfs.h>
#include <linux/writeback.h>
#include <linux/quotaops.h>
#include <linux/seq_file.h>
#include <linux/parser.h>
#include <linux/uio.h>
#include <linux/mman.h>
#include <linux/sched/mm.h>
#include <linux/crc32.h>
#include <linux/task_io_accounting_ops.h>
#include "zonefs.h"
static int zonefs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap,
struct iomap *srcmap)
{
struct zonefs_inode_info *zi = ZONEFS_I(inode);
struct super_block *sb = inode->i_sb;
loff_t isize;
/* All I/Os should always be within the file maximum size */
if (WARN_ON_ONCE(offset + length > zi->i_max_size))
return -EIO;
/*
* Sequential zones can only accept direct writes. This is already
* checked when writes are issued, so warn if we see a page writeback
* operation.
*/
if (WARN_ON_ONCE(zi->i_ztype == ZONEFS_ZTYPE_SEQ &&
(flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT)))
return -EIO;
/*
* For conventional zones, all blocks are always mapped. For sequential
* zones, all blocks after always mapped below the inode size (zone
* write pointer) and unwriten beyond.
*/
mutex_lock(&zi->i_truncate_mutex);
isize = i_size_read(inode);
if (offset >= isize)
iomap->type = IOMAP_UNWRITTEN;
else
iomap->type = IOMAP_MAPPED;
if (flags & IOMAP_WRITE)
length = zi->i_max_size - offset;
else
length = min(length, isize - offset);
mutex_unlock(&zi->i_truncate_mutex);
iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize);
iomap->length = ALIGN(offset + length, sb->s_blocksize) - iomap->offset;
iomap->bdev = inode->i_sb->s_bdev;
iomap->addr = (zi->i_zsector << SECTOR_SHIFT) + iomap->offset;
return 0;
}
static const struct iomap_ops zonefs_iomap_ops = {
.iomap_begin = zonefs_iomap_begin,
};
static int zonefs_readpage(struct file *unused, struct page *page)
{
return iomap_readpage(page, &zonefs_iomap_ops);
}
static void zonefs_readahead(struct readahead_control *rac)
{
iomap_readahead(rac, &zonefs_iomap_ops);
}
/*
* Map blocks for page writeback. This is used only on conventional zone files,
* which implies that the page range can only be within the fixed inode size.
*/
static int zonefs_map_blocks(struct iomap_writepage_ctx *wpc,
struct inode *inode, loff_t offset)
{
struct zonefs_inode_info *zi = ZONEFS_I(inode);
if (WARN_ON_ONCE(zi->i_ztype != ZONEFS_ZTYPE_CNV))
return -EIO;
if (WARN_ON_ONCE(offset >= i_size_read(inode)))
return -EIO;
/* If the mapping is already OK, nothing needs to be done */
if (offset >= wpc->iomap.offset &&
offset < wpc->iomap.offset + wpc->iomap.length)
return 0;
return zonefs_iomap_begin(inode, offset, zi->i_max_size - offset,
IOMAP_WRITE, &wpc->iomap, NULL);
}
static const struct iomap_writeback_ops zonefs_writeback_ops = {
.map_blocks = zonefs_map_blocks,
};
static int zonefs_writepage(struct page *page, struct writeback_control *wbc)
{
struct iomap_writepage_ctx wpc = { };
return iomap_writepage(page, wbc, &wpc, &zonefs_writeback_ops);
}
static int zonefs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct iomap_writepage_ctx wpc = { };
return iomap_writepages(mapping, wbc, &wpc, &zonefs_writeback_ops);
}
static const struct address_space_operations zonefs_file_aops = {
.readpage = zonefs_readpage,
.readahead = zonefs_readahead,
.writepage = zonefs_writepage,
.writepages = zonefs_writepages,
.set_page_dirty = iomap_set_page_dirty,
.releasepage = iomap_releasepage,
.invalidatepage = iomap_invalidatepage,
.migratepage = iomap_migrate_page,
.is_partially_uptodate = iomap_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
.direct_IO = noop_direct_IO,
};
static void zonefs_update_stats(struct inode *inode, loff_t new_isize)
{
struct super_block *sb = inode->i_sb;
struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
loff_t old_isize = i_size_read(inode);
loff_t nr_blocks;
if (new_isize == old_isize)
return;
spin_lock(&sbi->s_lock);
/*
* This may be called for an update after an IO error.
* So beware of the values seen.
*/
if (new_isize < old_isize) {
nr_blocks = (old_isize - new_isize) >> sb->s_blocksize_bits;
if (sbi->s_used_blocks > nr_blocks)
sbi->s_used_blocks -= nr_blocks;
else
sbi->s_used_blocks = 0;
} else {
sbi->s_used_blocks +=
(new_isize - old_isize) >> sb->s_blocksize_bits;
if (sbi->s_used_blocks > sbi->s_blocks)
sbi->s_used_blocks = sbi->s_blocks;
}
spin_unlock(&sbi->s_lock);
}
/*
* Check a zone condition and adjust its file inode access permissions for
* offline and readonly zones. Return the inode size corresponding to the
* amount of readable data in the zone.
*/
static loff_t zonefs_check_zone_condition(struct inode *inode,
struct blk_zone *zone, bool warn,
bool mount)
{
struct zonefs_inode_info *zi = ZONEFS_I(inode);
switch (zone->cond) {
case BLK_ZONE_COND_OFFLINE:
/*
* Dead zone: make the inode immutable, disable all accesses
* and set the file size to 0 (zone wp set to zone start).
*/
if (warn)
zonefs_warn(inode->i_sb, "inode %lu: offline zone\n",
inode->i_ino);
inode->i_flags |= S_IMMUTABLE;
inode->i_mode &= ~0777;
zone->wp = zone->start;
return 0;
case BLK_ZONE_COND_READONLY:
/*
* 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)
zonefs_warn(inode->i_sb, "inode %lu: read-only zone\n",
inode->i_ino);
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;
return i_size_read(inode);
default:
if (zi->i_ztype == ZONEFS_ZTYPE_CNV)
return zi->i_max_size;
return (zone->wp - zone->start) << SECTOR_SHIFT;
}
}
struct zonefs_ioerr_data {
struct inode *inode;
bool write;
};
static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
void *data)
{
struct zonefs_ioerr_data *err = data;
struct inode *inode = err->inode;
struct zonefs_inode_info *zi = ZONEFS_I(inode);
struct super_block *sb = inode->i_sb;
struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
loff_t isize, data_size;
/*
* Check the zone condition: if the zone is not "bad" (offline or
* read-only), read errors are simply signaled to the IO issuer as long
* as there is no inconsistency between the inode size and the amount of
* data writen in the zone (data_size).
*/
data_size = zonefs_check_zone_condition(inode, zone, true, false);
isize = i_size_read(inode);
if (zone->cond != BLK_ZONE_COND_OFFLINE &&
zone->cond != BLK_ZONE_COND_READONLY &&
!err->write && isize == data_size)
return 0;
/*
* At this point, we detected either a bad zone or an inconsistency
* between the inode size and the amount of data written in the zone.
* For the latter case, the cause may be a write IO error or an external
* action on the device. Two error patterns exist:
* 1) The inode size is lower than the amount of data in the zone:
* a write operation partially f
|