162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Simple file system for zoned block devices exposing zones as files. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 Western Digital Corporation or its affiliates. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/pagemap.h> 962306a36Sopenharmony_ci#include <linux/iomap.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/blkdev.h> 1362306a36Sopenharmony_ci#include <linux/statfs.h> 1462306a36Sopenharmony_ci#include <linux/writeback.h> 1562306a36Sopenharmony_ci#include <linux/quotaops.h> 1662306a36Sopenharmony_ci#include <linux/seq_file.h> 1762306a36Sopenharmony_ci#include <linux/parser.h> 1862306a36Sopenharmony_ci#include <linux/uio.h> 1962306a36Sopenharmony_ci#include <linux/mman.h> 2062306a36Sopenharmony_ci#include <linux/sched/mm.h> 2162306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "zonefs.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "trace.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int zonefs_read_iomap_begin(struct inode *inode, loff_t offset, 2862306a36Sopenharmony_ci loff_t length, unsigned int flags, 2962306a36Sopenharmony_ci struct iomap *iomap, struct iomap *srcmap) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 3262306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 3362306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 3462306a36Sopenharmony_ci loff_t isize; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* 3762306a36Sopenharmony_ci * All blocks are always mapped below EOF. If reading past EOF, 3862306a36Sopenharmony_ci * act as if there is a hole up to the file maximum size. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 4162306a36Sopenharmony_ci iomap->bdev = inode->i_sb->s_bdev; 4262306a36Sopenharmony_ci iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize); 4362306a36Sopenharmony_ci isize = i_size_read(inode); 4462306a36Sopenharmony_ci if (iomap->offset >= isize) { 4562306a36Sopenharmony_ci iomap->type = IOMAP_HOLE; 4662306a36Sopenharmony_ci iomap->addr = IOMAP_NULL_ADDR; 4762306a36Sopenharmony_ci iomap->length = length; 4862306a36Sopenharmony_ci } else { 4962306a36Sopenharmony_ci iomap->type = IOMAP_MAPPED; 5062306a36Sopenharmony_ci iomap->addr = (z->z_sector << SECTOR_SHIFT) + iomap->offset; 5162306a36Sopenharmony_ci iomap->length = isize - iomap->offset; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci trace_zonefs_iomap_begin(inode, iomap); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct iomap_ops zonefs_read_iomap_ops = { 6162306a36Sopenharmony_ci .iomap_begin = zonefs_read_iomap_begin, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int zonefs_write_iomap_begin(struct inode *inode, loff_t offset, 6562306a36Sopenharmony_ci loff_t length, unsigned int flags, 6662306a36Sopenharmony_ci struct iomap *iomap, struct iomap *srcmap) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 6962306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 7062306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 7162306a36Sopenharmony_ci loff_t isize; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* All write I/Os should always be within the file maximum size */ 7462306a36Sopenharmony_ci if (WARN_ON_ONCE(offset + length > z->z_capacity)) 7562306a36Sopenharmony_ci return -EIO; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Sequential zones can only accept direct writes. This is already 7962306a36Sopenharmony_ci * checked when writes are issued, so warn if we see a page writeback 8062306a36Sopenharmony_ci * operation. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci if (WARN_ON_ONCE(zonefs_zone_is_seq(z) && !(flags & IOMAP_DIRECT))) 8362306a36Sopenharmony_ci return -EIO; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * For conventional zones, all blocks are always mapped. For sequential 8762306a36Sopenharmony_ci * zones, all blocks after always mapped below the inode size (zone 8862306a36Sopenharmony_ci * write pointer) and unwriten beyond. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 9162306a36Sopenharmony_ci iomap->bdev = inode->i_sb->s_bdev; 9262306a36Sopenharmony_ci iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize); 9362306a36Sopenharmony_ci iomap->addr = (z->z_sector << SECTOR_SHIFT) + iomap->offset; 9462306a36Sopenharmony_ci isize = i_size_read(inode); 9562306a36Sopenharmony_ci if (iomap->offset >= isize) { 9662306a36Sopenharmony_ci iomap->type = IOMAP_UNWRITTEN; 9762306a36Sopenharmony_ci iomap->length = z->z_capacity - iomap->offset; 9862306a36Sopenharmony_ci } else { 9962306a36Sopenharmony_ci iomap->type = IOMAP_MAPPED; 10062306a36Sopenharmony_ci iomap->length = isize - iomap->offset; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci trace_zonefs_iomap_begin(inode, iomap); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct iomap_ops zonefs_write_iomap_ops = { 11062306a36Sopenharmony_ci .iomap_begin = zonefs_write_iomap_begin, 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int zonefs_read_folio(struct file *unused, struct folio *folio) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return iomap_read_folio(folio, &zonefs_read_iomap_ops); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void zonefs_readahead(struct readahead_control *rac) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci iomap_readahead(rac, &zonefs_read_iomap_ops); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Map blocks for page writeback. This is used only on conventional zone files, 12562306a36Sopenharmony_ci * which implies that the page range can only be within the fixed inode size. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic int zonefs_write_map_blocks(struct iomap_writepage_ctx *wpc, 12862306a36Sopenharmony_ci struct inode *inode, loff_t offset) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (WARN_ON_ONCE(zonefs_zone_is_seq(z))) 13362306a36Sopenharmony_ci return -EIO; 13462306a36Sopenharmony_ci if (WARN_ON_ONCE(offset >= i_size_read(inode))) 13562306a36Sopenharmony_ci return -EIO; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* If the mapping is already OK, nothing needs to be done */ 13862306a36Sopenharmony_ci if (offset >= wpc->iomap.offset && 13962306a36Sopenharmony_ci offset < wpc->iomap.offset + wpc->iomap.length) 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return zonefs_write_iomap_begin(inode, offset, 14362306a36Sopenharmony_ci z->z_capacity - offset, 14462306a36Sopenharmony_ci IOMAP_WRITE, &wpc->iomap, NULL); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const struct iomap_writeback_ops zonefs_writeback_ops = { 14862306a36Sopenharmony_ci .map_blocks = zonefs_write_map_blocks, 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int zonefs_writepages(struct address_space *mapping, 15262306a36Sopenharmony_ci struct writeback_control *wbc) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct iomap_writepage_ctx wpc = { }; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return iomap_writepages(mapping, wbc, &wpc, &zonefs_writeback_ops); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int zonefs_swap_activate(struct swap_info_struct *sis, 16062306a36Sopenharmony_ci struct file *swap_file, sector_t *span) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct inode *inode = file_inode(swap_file); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (zonefs_inode_is_seq(inode)) { 16562306a36Sopenharmony_ci zonefs_err(inode->i_sb, 16662306a36Sopenharmony_ci "swap file: not a conventional zone file\n"); 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return iomap_swapfile_activate(sis, swap_file, span, 17162306a36Sopenharmony_ci &zonefs_read_iomap_ops); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciconst struct address_space_operations zonefs_file_aops = { 17562306a36Sopenharmony_ci .read_folio = zonefs_read_folio, 17662306a36Sopenharmony_ci .readahead = zonefs_readahead, 17762306a36Sopenharmony_ci .writepages = zonefs_writepages, 17862306a36Sopenharmony_ci .dirty_folio = iomap_dirty_folio, 17962306a36Sopenharmony_ci .release_folio = iomap_release_folio, 18062306a36Sopenharmony_ci .invalidate_folio = iomap_invalidate_folio, 18162306a36Sopenharmony_ci .migrate_folio = filemap_migrate_folio, 18262306a36Sopenharmony_ci .is_partially_uptodate = iomap_is_partially_uptodate, 18362306a36Sopenharmony_ci .error_remove_page = generic_error_remove_page, 18462306a36Sopenharmony_ci .swap_activate = zonefs_swap_activate, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciint zonefs_file_truncate(struct inode *inode, loff_t isize) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 19062306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 19162306a36Sopenharmony_ci loff_t old_isize; 19262306a36Sopenharmony_ci enum req_op op; 19362306a36Sopenharmony_ci int ret = 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Only sequential zone files can be truncated and truncation is allowed 19762306a36Sopenharmony_ci * only down to a 0 size, which is equivalent to a zone reset, and to 19862306a36Sopenharmony_ci * the maximum file size, which is equivalent to a zone finish. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if (!zonefs_zone_is_seq(z)) 20162306a36Sopenharmony_ci return -EPERM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!isize) 20462306a36Sopenharmony_ci op = REQ_OP_ZONE_RESET; 20562306a36Sopenharmony_ci else if (isize == z->z_capacity) 20662306a36Sopenharmony_ci op = REQ_OP_ZONE_FINISH; 20762306a36Sopenharmony_ci else 20862306a36Sopenharmony_ci return -EPERM; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci inode_dio_wait(inode); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Serialize against page faults */ 21362306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Serialize against zonefs_iomap_begin() */ 21662306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci old_isize = i_size_read(inode); 21962306a36Sopenharmony_ci if (isize == old_isize) 22062306a36Sopenharmony_ci goto unlock; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = zonefs_inode_zone_mgmt(inode, op); 22362306a36Sopenharmony_ci if (ret) 22462306a36Sopenharmony_ci goto unlock; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * If the mount option ZONEFS_MNTOPT_EXPLICIT_OPEN is set, 22862306a36Sopenharmony_ci * take care of open zones. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_OPEN) { 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Truncating a zone to EMPTY or FULL is the equivalent of 23362306a36Sopenharmony_ci * closing the zone. For a truncation to 0, we need to 23462306a36Sopenharmony_ci * re-open the zone to ensure new writes can be processed. 23562306a36Sopenharmony_ci * For a truncation to the maximum file size, the zone is 23662306a36Sopenharmony_ci * closed and writes cannot be accepted anymore, so clear 23762306a36Sopenharmony_ci * the open flag. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci if (!isize) 24062306a36Sopenharmony_ci ret = zonefs_inode_zone_mgmt(inode, REQ_OP_ZONE_OPEN); 24162306a36Sopenharmony_ci else 24262306a36Sopenharmony_ci z->z_flags &= ~ZONEFS_ZONE_OPEN; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci zonefs_update_stats(inode, isize); 24662306a36Sopenharmony_ci truncate_setsize(inode, isize); 24762306a36Sopenharmony_ci z->z_wpoffset = isize; 24862306a36Sopenharmony_ci zonefs_inode_account_active(inode); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciunlock: 25162306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 25262306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int zonefs_file_fsync(struct file *file, loff_t start, loff_t end, 25862306a36Sopenharmony_ci int datasync) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 26162306a36Sopenharmony_ci int ret = 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (unlikely(IS_IMMUTABLE(inode))) 26462306a36Sopenharmony_ci return -EPERM; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * Since only direct writes are allowed in sequential files, page cache 26862306a36Sopenharmony_ci * flush is needed only for conventional zone files. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci if (zonefs_inode_is_cnv(inode)) 27162306a36Sopenharmony_ci ret = file_write_and_wait_range(file, start, end); 27262306a36Sopenharmony_ci if (!ret) 27362306a36Sopenharmony_ci ret = blkdev_issue_flush(inode->i_sb->s_bdev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (ret) 27662306a36Sopenharmony_ci zonefs_io_error(inode, true); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic vm_fault_t zonefs_filemap_page_mkwrite(struct vm_fault *vmf) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct inode *inode = file_inode(vmf->vma->vm_file); 28462306a36Sopenharmony_ci vm_fault_t ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (unlikely(IS_IMMUTABLE(inode))) 28762306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * Sanity check: only conventional zone files can have shared 29162306a36Sopenharmony_ci * writeable mappings. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci if (zonefs_inode_is_seq(inode)) 29462306a36Sopenharmony_ci return VM_FAULT_NOPAGE; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci sb_start_pagefault(inode->i_sb); 29762306a36Sopenharmony_ci file_update_time(vmf->vma->vm_file); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Serialize against truncates */ 30062306a36Sopenharmony_ci filemap_invalidate_lock_shared(inode->i_mapping); 30162306a36Sopenharmony_ci ret = iomap_page_mkwrite(vmf, &zonefs_write_iomap_ops); 30262306a36Sopenharmony_ci filemap_invalidate_unlock_shared(inode->i_mapping); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sb_end_pagefault(inode->i_sb); 30562306a36Sopenharmony_ci return ret; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic const struct vm_operations_struct zonefs_file_vm_ops = { 30962306a36Sopenharmony_ci .fault = filemap_fault, 31062306a36Sopenharmony_ci .map_pages = filemap_map_pages, 31162306a36Sopenharmony_ci .page_mkwrite = zonefs_filemap_page_mkwrite, 31262306a36Sopenharmony_ci}; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int zonefs_file_mmap(struct file *file, struct vm_area_struct *vma) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * Conventional zones accept random writes, so their files can support 31862306a36Sopenharmony_ci * shared writable mappings. For sequential zone files, only read 31962306a36Sopenharmony_ci * mappings are possible since there are no guarantees for write 32062306a36Sopenharmony_ci * ordering between msync() and page cache writeback. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci if (zonefs_inode_is_seq(file_inode(file)) && 32362306a36Sopenharmony_ci (vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) 32462306a36Sopenharmony_ci return -EINVAL; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci file_accessed(file); 32762306a36Sopenharmony_ci vma->vm_ops = &zonefs_file_vm_ops; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic loff_t zonefs_file_llseek(struct file *file, loff_t offset, int whence) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci loff_t isize = i_size_read(file_inode(file)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * Seeks are limited to below the zone size for conventional zones 33862306a36Sopenharmony_ci * and below the zone write pointer for sequential zones. In both 33962306a36Sopenharmony_ci * cases, this limit is the inode size. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci return generic_file_llseek_size(file, offset, whence, isize, isize); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, 34562306a36Sopenharmony_ci int error, unsigned int flags) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct inode *inode = file_inode(iocb->ki_filp); 34862306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (error) { 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * For Sync IOs, error recovery is called from 35362306a36Sopenharmony_ci * zonefs_file_dio_write(). 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci if (!is_sync_kiocb(iocb)) 35662306a36Sopenharmony_ci zonefs_io_error(inode, true); 35762306a36Sopenharmony_ci return error; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (size && zonefs_inode_is_seq(inode)) { 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * Note that we may be seeing completions out of order, 36362306a36Sopenharmony_ci * but that is not a problem since a write completed 36462306a36Sopenharmony_ci * successfully necessarily means that all preceding writes 36562306a36Sopenharmony_ci * were also successful. So we can safely increase the inode 36662306a36Sopenharmony_ci * size to the write end location. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 36962306a36Sopenharmony_ci if (i_size_read(inode) < iocb->ki_pos + size) { 37062306a36Sopenharmony_ci zonefs_update_stats(inode, iocb->ki_pos + size); 37162306a36Sopenharmony_ci zonefs_i_size_write(inode, iocb->ki_pos + size); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic const struct iomap_dio_ops zonefs_write_dio_ops = { 38062306a36Sopenharmony_ci .end_io = zonefs_file_write_dio_end_io, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * Do not exceed the LFS limits nor the file zone size. If pos is under the 38562306a36Sopenharmony_ci * limit it becomes a short access. If it exceeds the limit, return -EFBIG. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_cistatic loff_t zonefs_write_check_limits(struct file *file, loff_t pos, 38862306a36Sopenharmony_ci loff_t count) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 39162306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 39262306a36Sopenharmony_ci loff_t limit = rlimit(RLIMIT_FSIZE); 39362306a36Sopenharmony_ci loff_t max_size = z->z_capacity; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (limit != RLIM_INFINITY) { 39662306a36Sopenharmony_ci if (pos >= limit) { 39762306a36Sopenharmony_ci send_sig(SIGXFSZ, current, 0); 39862306a36Sopenharmony_ci return -EFBIG; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci count = min(count, limit - pos); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (!(file->f_flags & O_LARGEFILE)) 40462306a36Sopenharmony_ci max_size = min_t(loff_t, MAX_NON_LFS, max_size); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (unlikely(pos >= max_size)) 40762306a36Sopenharmony_ci return -EFBIG; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return min(count, max_size - pos); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic ssize_t zonefs_write_checks(struct kiocb *iocb, struct iov_iter *from) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct file *file = iocb->ki_filp; 41562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 41662306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 41762306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 41862306a36Sopenharmony_ci loff_t count; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (IS_SWAPFILE(inode)) 42162306a36Sopenharmony_ci return -ETXTBSY; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!iov_iter_count(from)) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (iocb->ki_flags & IOCB_APPEND) { 43062306a36Sopenharmony_ci if (zonefs_zone_is_cnv(z)) 43162306a36Sopenharmony_ci return -EINVAL; 43262306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 43362306a36Sopenharmony_ci iocb->ki_pos = z->z_wpoffset; 43462306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci count = zonefs_write_check_limits(file, iocb->ki_pos, 43862306a36Sopenharmony_ci iov_iter_count(from)); 43962306a36Sopenharmony_ci if (count < 0) 44062306a36Sopenharmony_ci return count; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci iov_iter_truncate(from, count); 44362306a36Sopenharmony_ci return iov_iter_count(from); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * Handle direct writes. For sequential zone files, this is the only possible 44862306a36Sopenharmony_ci * write path. For these files, check that the user is issuing writes 44962306a36Sopenharmony_ci * sequentially from the end of the file. This code assumes that the block layer 45062306a36Sopenharmony_ci * delivers write requests to the device in sequential order. This is always the 45162306a36Sopenharmony_ci * case if a block IO scheduler implementing the ELEVATOR_F_ZBD_SEQ_WRITE 45262306a36Sopenharmony_ci * elevator feature is being used (e.g. mq-deadline). The block layer always 45362306a36Sopenharmony_ci * automatically select such an elevator for zoned block devices during the 45462306a36Sopenharmony_ci * device initialization. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct inode *inode = file_inode(iocb->ki_filp); 45962306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 46062306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 46162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 46262306a36Sopenharmony_ci ssize_t ret, count; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * For async direct IOs to sequential zone files, refuse IOCB_NOWAIT 46662306a36Sopenharmony_ci * as this can cause write reordering (e.g. the first aio gets EAGAIN 46762306a36Sopenharmony_ci * on the inode lock but the second goes through but is now unaligned). 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci if (zonefs_zone_is_seq(z) && !is_sync_kiocb(iocb) && 47062306a36Sopenharmony_ci (iocb->ki_flags & IOCB_NOWAIT)) 47162306a36Sopenharmony_ci return -EOPNOTSUPP; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (iocb->ki_flags & IOCB_NOWAIT) { 47462306a36Sopenharmony_ci if (!inode_trylock(inode)) 47562306a36Sopenharmony_ci return -EAGAIN; 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci inode_lock(inode); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci count = zonefs_write_checks(iocb, from); 48162306a36Sopenharmony_ci if (count <= 0) { 48262306a36Sopenharmony_ci ret = count; 48362306a36Sopenharmony_ci goto inode_unlock; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { 48762306a36Sopenharmony_ci ret = -EINVAL; 48862306a36Sopenharmony_ci goto inode_unlock; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* Enforce sequential writes (append only) in sequential zones */ 49262306a36Sopenharmony_ci if (zonefs_zone_is_seq(z)) { 49362306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 49462306a36Sopenharmony_ci if (iocb->ki_pos != z->z_wpoffset) { 49562306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 49662306a36Sopenharmony_ci ret = -EINVAL; 49762306a36Sopenharmony_ci goto inode_unlock; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * Advance the zone write pointer offset. This assumes that the 50162306a36Sopenharmony_ci * IO will succeed, which is OK to do because we do not allow 50262306a36Sopenharmony_ci * partial writes (IOMAP_DIO_PARTIAL is not set) and if the IO 50362306a36Sopenharmony_ci * fails, the error path will correct the write pointer offset. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci z->z_wpoffset += count; 50662306a36Sopenharmony_ci zonefs_inode_account_active(inode); 50762306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* 51162306a36Sopenharmony_ci * iomap_dio_rw() may return ENOTBLK if there was an issue with 51262306a36Sopenharmony_ci * page invalidation. Overwrite that error code with EBUSY so that 51362306a36Sopenharmony_ci * the user can make sense of the error. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci ret = iomap_dio_rw(iocb, from, &zonefs_write_iomap_ops, 51662306a36Sopenharmony_ci &zonefs_write_dio_ops, 0, NULL, 0); 51762306a36Sopenharmony_ci if (ret == -ENOTBLK) 51862306a36Sopenharmony_ci ret = -EBUSY; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * For a failed IO or partial completion, trigger error recovery 52262306a36Sopenharmony_ci * to update the zone write pointer offset to a correct value. 52362306a36Sopenharmony_ci * For asynchronous IOs, zonefs_file_write_dio_end_io() may already 52462306a36Sopenharmony_ci * have executed error recovery if the IO already completed when we 52562306a36Sopenharmony_ci * reach here. However, we cannot know that and execute error recovery 52662306a36Sopenharmony_ci * again (that will not change anything). 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci if (zonefs_zone_is_seq(z)) { 52962306a36Sopenharmony_ci if (ret > 0 && ret != count) 53062306a36Sopenharmony_ci ret = -EIO; 53162306a36Sopenharmony_ci if (ret < 0 && ret != -EIOCBQUEUED) 53262306a36Sopenharmony_ci zonefs_io_error(inode, true); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ciinode_unlock: 53662306a36Sopenharmony_ci inode_unlock(inode); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return ret; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic ssize_t zonefs_file_buffered_write(struct kiocb *iocb, 54262306a36Sopenharmony_ci struct iov_iter *from) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct inode *inode = file_inode(iocb->ki_filp); 54562306a36Sopenharmony_ci ssize_t ret; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * Direct IO writes are mandatory for sequential zone files so that the 54962306a36Sopenharmony_ci * write IO issuing order is preserved. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci if (zonefs_inode_is_seq(inode)) 55262306a36Sopenharmony_ci return -EIO; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (iocb->ki_flags & IOCB_NOWAIT) { 55562306a36Sopenharmony_ci if (!inode_trylock(inode)) 55662306a36Sopenharmony_ci return -EAGAIN; 55762306a36Sopenharmony_ci } else { 55862306a36Sopenharmony_ci inode_lock(inode); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci ret = zonefs_write_checks(iocb, from); 56262306a36Sopenharmony_ci if (ret <= 0) 56362306a36Sopenharmony_ci goto inode_unlock; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = iomap_file_buffered_write(iocb, from, &zonefs_write_iomap_ops); 56662306a36Sopenharmony_ci if (ret == -EIO) 56762306a36Sopenharmony_ci zonefs_io_error(inode, true); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ciinode_unlock: 57062306a36Sopenharmony_ci inode_unlock(inode); 57162306a36Sopenharmony_ci if (ret > 0) 57262306a36Sopenharmony_ci ret = generic_write_sync(iocb, ret); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic ssize_t zonefs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct inode *inode = file_inode(iocb->ki_filp); 58062306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (unlikely(IS_IMMUTABLE(inode))) 58362306a36Sopenharmony_ci return -EPERM; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (sb_rdonly(inode->i_sb)) 58662306a36Sopenharmony_ci return -EROFS; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Write operations beyond the zone capacity are not allowed */ 58962306a36Sopenharmony_ci if (iocb->ki_pos >= z->z_capacity) 59062306a36Sopenharmony_ci return -EFBIG; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (iocb->ki_flags & IOCB_DIRECT) { 59362306a36Sopenharmony_ci ssize_t ret = zonefs_file_dio_write(iocb, from); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (ret != -ENOTBLK) 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return zonefs_file_buffered_write(iocb, from); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int zonefs_file_read_dio_end_io(struct kiocb *iocb, ssize_t size, 60362306a36Sopenharmony_ci int error, unsigned int flags) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci if (error) { 60662306a36Sopenharmony_ci zonefs_io_error(file_inode(iocb->ki_filp), false); 60762306a36Sopenharmony_ci return error; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic const struct iomap_dio_ops zonefs_read_dio_ops = { 61462306a36Sopenharmony_ci .end_io = zonefs_file_read_dio_end_io, 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic ssize_t zonefs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct inode *inode = file_inode(iocb->ki_filp); 62062306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 62162306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 62262306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 62362306a36Sopenharmony_ci loff_t isize; 62462306a36Sopenharmony_ci ssize_t ret; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Offline zones cannot be read */ 62762306a36Sopenharmony_ci if (unlikely(IS_IMMUTABLE(inode) && !(inode->i_mode & 0777))) 62862306a36Sopenharmony_ci return -EPERM; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (iocb->ki_pos >= z->z_capacity) 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (iocb->ki_flags & IOCB_NOWAIT) { 63462306a36Sopenharmony_ci if (!inode_trylock_shared(inode)) 63562306a36Sopenharmony_ci return -EAGAIN; 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci inode_lock_shared(inode); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Limit read operations to written data */ 64162306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 64262306a36Sopenharmony_ci isize = i_size_read(inode); 64362306a36Sopenharmony_ci if (iocb->ki_pos >= isize) { 64462306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 64562306a36Sopenharmony_ci ret = 0; 64662306a36Sopenharmony_ci goto inode_unlock; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci iov_iter_truncate(to, isize - iocb->ki_pos); 64962306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (iocb->ki_flags & IOCB_DIRECT) { 65262306a36Sopenharmony_ci size_t count = iov_iter_count(to); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { 65562306a36Sopenharmony_ci ret = -EINVAL; 65662306a36Sopenharmony_ci goto inode_unlock; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci file_accessed(iocb->ki_filp); 65962306a36Sopenharmony_ci ret = iomap_dio_rw(iocb, to, &zonefs_read_iomap_ops, 66062306a36Sopenharmony_ci &zonefs_read_dio_ops, 0, NULL, 0); 66162306a36Sopenharmony_ci } else { 66262306a36Sopenharmony_ci ret = generic_file_read_iter(iocb, to); 66362306a36Sopenharmony_ci if (ret == -EIO) 66462306a36Sopenharmony_ci zonefs_io_error(inode, false); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ciinode_unlock: 66862306a36Sopenharmony_ci inode_unlock_shared(inode); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return ret; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic ssize_t zonefs_file_splice_read(struct file *in, loff_t *ppos, 67462306a36Sopenharmony_ci struct pipe_inode_info *pipe, 67562306a36Sopenharmony_ci size_t len, unsigned int flags) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct inode *inode = file_inode(in); 67862306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 67962306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 68062306a36Sopenharmony_ci loff_t isize; 68162306a36Sopenharmony_ci ssize_t ret = 0; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Offline zones cannot be read */ 68462306a36Sopenharmony_ci if (unlikely(IS_IMMUTABLE(inode) && !(inode->i_mode & 0777))) 68562306a36Sopenharmony_ci return -EPERM; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (*ppos >= z->z_capacity) 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci inode_lock_shared(inode); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Limit read operations to written data */ 69362306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 69462306a36Sopenharmony_ci isize = i_size_read(inode); 69562306a36Sopenharmony_ci if (*ppos >= isize) 69662306a36Sopenharmony_ci len = 0; 69762306a36Sopenharmony_ci else 69862306a36Sopenharmony_ci len = min_t(loff_t, len, isize - *ppos); 69962306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (len > 0) { 70262306a36Sopenharmony_ci ret = filemap_splice_read(in, ppos, pipe, len, flags); 70362306a36Sopenharmony_ci if (ret == -EIO) 70462306a36Sopenharmony_ci zonefs_io_error(inode, false); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci inode_unlock_shared(inode); 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/* 71262306a36Sopenharmony_ci * Write open accounting is done only for sequential files. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_cistatic inline bool zonefs_seq_file_need_wro(struct inode *inode, 71562306a36Sopenharmony_ci struct file *file) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci if (zonefs_inode_is_cnv(inode)) 71862306a36Sopenharmony_ci return false; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!(file->f_mode & FMODE_WRITE)) 72162306a36Sopenharmony_ci return false; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return true; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int zonefs_seq_file_write_open(struct inode *inode) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 72962306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 73062306a36Sopenharmony_ci int ret = 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!zi->i_wr_refcnt) { 73562306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); 73662306a36Sopenharmony_ci unsigned int wro = atomic_inc_return(&sbi->s_wro_seq_files); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) { 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (sbi->s_max_wro_seq_files 74162306a36Sopenharmony_ci && wro > sbi->s_max_wro_seq_files) { 74262306a36Sopenharmony_ci atomic_dec(&sbi->s_wro_seq_files); 74362306a36Sopenharmony_ci ret = -EBUSY; 74462306a36Sopenharmony_ci goto unlock; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (i_size_read(inode) < z->z_capacity) { 74862306a36Sopenharmony_ci ret = zonefs_inode_zone_mgmt(inode, 74962306a36Sopenharmony_ci REQ_OP_ZONE_OPEN); 75062306a36Sopenharmony_ci if (ret) { 75162306a36Sopenharmony_ci atomic_dec(&sbi->s_wro_seq_files); 75262306a36Sopenharmony_ci goto unlock; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_OPEN; 75562306a36Sopenharmony_ci zonefs_inode_account_active(inode); 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci zi->i_wr_refcnt++; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ciunlock: 76362306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return ret; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int zonefs_file_open(struct inode *inode, struct file *file) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci int ret; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci file->f_mode |= FMODE_CAN_ODIRECT; 77362306a36Sopenharmony_ci ret = generic_file_open(inode, file); 77462306a36Sopenharmony_ci if (ret) 77562306a36Sopenharmony_ci return ret; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (zonefs_seq_file_need_wro(inode, file)) 77862306a36Sopenharmony_ci return zonefs_seq_file_write_open(inode); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic void zonefs_seq_file_write_close(struct inode *inode) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct zonefs_inode_info *zi = ZONEFS_I(inode); 78662306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 78762306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 78862306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 78962306a36Sopenharmony_ci int ret = 0; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci mutex_lock(&zi->i_truncate_mutex); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci zi->i_wr_refcnt--; 79462306a36Sopenharmony_ci if (zi->i_wr_refcnt) 79562306a36Sopenharmony_ci goto unlock; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* 79862306a36Sopenharmony_ci * The file zone may not be open anymore (e.g. the file was truncated to 79962306a36Sopenharmony_ci * its maximum size or it was fully written). For this case, we only 80062306a36Sopenharmony_ci * need to decrement the write open count. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_OPEN) { 80362306a36Sopenharmony_ci ret = zonefs_inode_zone_mgmt(inode, REQ_OP_ZONE_CLOSE); 80462306a36Sopenharmony_ci if (ret) { 80562306a36Sopenharmony_ci __zonefs_io_error(inode, false); 80662306a36Sopenharmony_ci /* 80762306a36Sopenharmony_ci * Leaving zones explicitly open may lead to a state 80862306a36Sopenharmony_ci * where most zones cannot be written (zone resources 80962306a36Sopenharmony_ci * exhausted). So take preventive action by remounting 81062306a36Sopenharmony_ci * read-only. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_OPEN && 81362306a36Sopenharmony_ci !(sb->s_flags & SB_RDONLY)) { 81462306a36Sopenharmony_ci zonefs_warn(sb, 81562306a36Sopenharmony_ci "closing zone at %llu failed %d\n", 81662306a36Sopenharmony_ci z->z_sector, ret); 81762306a36Sopenharmony_ci zonefs_warn(sb, 81862306a36Sopenharmony_ci "remounting filesystem read-only\n"); 81962306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci goto unlock; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci z->z_flags &= ~ZONEFS_ZONE_OPEN; 82562306a36Sopenharmony_ci zonefs_inode_account_active(inode); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci atomic_dec(&sbi->s_wro_seq_files); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ciunlock: 83162306a36Sopenharmony_ci mutex_unlock(&zi->i_truncate_mutex); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int zonefs_file_release(struct inode *inode, struct file *file) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci /* 83762306a36Sopenharmony_ci * If we explicitly open a zone we must close it again as well, but the 83862306a36Sopenharmony_ci * zone management operation can fail (either due to an IO error or as 83962306a36Sopenharmony_ci * the zone has gone offline or read-only). Make sure we don't fail the 84062306a36Sopenharmony_ci * close(2) for user-space. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci if (zonefs_seq_file_need_wro(inode, file)) 84362306a36Sopenharmony_ci zonefs_seq_file_write_close(inode); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ciconst struct file_operations zonefs_file_operations = { 84962306a36Sopenharmony_ci .open = zonefs_file_open, 85062306a36Sopenharmony_ci .release = zonefs_file_release, 85162306a36Sopenharmony_ci .fsync = zonefs_file_fsync, 85262306a36Sopenharmony_ci .mmap = zonefs_file_mmap, 85362306a36Sopenharmony_ci .llseek = zonefs_file_llseek, 85462306a36Sopenharmony_ci .read_iter = zonefs_file_read_iter, 85562306a36Sopenharmony_ci .write_iter = zonefs_file_write_iter, 85662306a36Sopenharmony_ci .splice_read = zonefs_file_splice_read, 85762306a36Sopenharmony_ci .splice_write = iter_file_splice_write, 85862306a36Sopenharmony_ci .iopoll = iocb_bio_iopoll, 85962306a36Sopenharmony_ci}; 860