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) 2019 Western Digital Corporation or its affiliates. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/pagemap.h> 962306a36Sopenharmony_ci#include <linux/magic.h> 1062306a36Sopenharmony_ci#include <linux/iomap.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/blkdev.h> 1462306a36Sopenharmony_ci#include <linux/statfs.h> 1562306a36Sopenharmony_ci#include <linux/writeback.h> 1662306a36Sopenharmony_ci#include <linux/quotaops.h> 1762306a36Sopenharmony_ci#include <linux/seq_file.h> 1862306a36Sopenharmony_ci#include <linux/parser.h> 1962306a36Sopenharmony_ci#include <linux/uio.h> 2062306a36Sopenharmony_ci#include <linux/mman.h> 2162306a36Sopenharmony_ci#include <linux/sched/mm.h> 2262306a36Sopenharmony_ci#include <linux/crc32.h> 2362306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "zonefs.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 2862306a36Sopenharmony_ci#include "trace.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Get the name of a zone group directory. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic const char *zonefs_zgroup_name(enum zonefs_ztype ztype) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci switch (ztype) { 3662306a36Sopenharmony_ci case ZONEFS_ZTYPE_CNV: 3762306a36Sopenharmony_ci return "cnv"; 3862306a36Sopenharmony_ci case ZONEFS_ZTYPE_SEQ: 3962306a36Sopenharmony_ci return "seq"; 4062306a36Sopenharmony_ci default: 4162306a36Sopenharmony_ci WARN_ON_ONCE(1); 4262306a36Sopenharmony_ci return "???"; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Manage the active zone count. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic void zonefs_account_active(struct super_block *sb, 5062306a36Sopenharmony_ci struct zonefs_zone *z) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (zonefs_zone_is_cnv(z)) 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * For zones that transitioned to the offline or readonly condition, 5962306a36Sopenharmony_ci * we only need to clear the active state. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci if (z->z_flags & (ZONEFS_ZONE_OFFLINE | ZONEFS_ZONE_READONLY)) 6262306a36Sopenharmony_ci goto out; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * If the zone is active, that is, if it is explicitly open or 6662306a36Sopenharmony_ci * partially written, check if it was already accounted as active. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if ((z->z_flags & ZONEFS_ZONE_OPEN) || 6962306a36Sopenharmony_ci (z->z_wpoffset > 0 && z->z_wpoffset < z->z_capacity)) { 7062306a36Sopenharmony_ci if (!(z->z_flags & ZONEFS_ZONE_ACTIVE)) { 7162306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_ACTIVE; 7262306a36Sopenharmony_ci atomic_inc(&sbi->s_active_seq_files); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciout: 7862306a36Sopenharmony_ci /* The zone is not active. If it was, update the active count */ 7962306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_ACTIVE) { 8062306a36Sopenharmony_ci z->z_flags &= ~ZONEFS_ZONE_ACTIVE; 8162306a36Sopenharmony_ci atomic_dec(&sbi->s_active_seq_files); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * Manage the active zone count. Called with zi->i_truncate_mutex held. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_civoid zonefs_inode_account_active(struct inode *inode) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci lockdep_assert_held(&ZONEFS_I(inode)->i_truncate_mutex); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return zonefs_account_active(inode->i_sb, zonefs_inode_zone(inode)); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Execute a zone management operation. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic int zonefs_zone_mgmt(struct super_block *sb, 9962306a36Sopenharmony_ci struct zonefs_zone *z, enum req_op op) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci int ret; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * With ZNS drives, closing an explicitly open zone that has not been 10562306a36Sopenharmony_ci * written will change the zone state to "closed", that is, the zone 10662306a36Sopenharmony_ci * will remain active. Since this can then cause failure of explicit 10762306a36Sopenharmony_ci * open operation on other zones if the drive active zone resources 10862306a36Sopenharmony_ci * are exceeded, make sure that the zone does not remain active by 10962306a36Sopenharmony_ci * resetting it. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci if (op == REQ_OP_ZONE_CLOSE && !z->z_wpoffset) 11262306a36Sopenharmony_ci op = REQ_OP_ZONE_RESET; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci trace_zonefs_zone_mgmt(sb, z, op); 11562306a36Sopenharmony_ci ret = blkdev_zone_mgmt(sb->s_bdev, op, z->z_sector, 11662306a36Sopenharmony_ci z->z_size >> SECTOR_SHIFT, GFP_NOFS); 11762306a36Sopenharmony_ci if (ret) { 11862306a36Sopenharmony_ci zonefs_err(sb, 11962306a36Sopenharmony_ci "Zone management operation %s at %llu failed %d\n", 12062306a36Sopenharmony_ci blk_op_str(op), z->z_sector, ret); 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint zonefs_inode_zone_mgmt(struct inode *inode, enum req_op op) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci lockdep_assert_held(&ZONEFS_I(inode)->i_truncate_mutex); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return zonefs_zone_mgmt(inode->i_sb, zonefs_inode_zone(inode), op); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_civoid zonefs_i_size_write(struct inode *inode, loff_t isize) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci i_size_write(inode, isize); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * A full zone is no longer open/active and does not need 14262306a36Sopenharmony_ci * explicit closing. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci if (isize >= z->z_capacity) { 14562306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_ACTIVE) 14862306a36Sopenharmony_ci atomic_dec(&sbi->s_active_seq_files); 14962306a36Sopenharmony_ci z->z_flags &= ~(ZONEFS_ZONE_OPEN | ZONEFS_ZONE_ACTIVE); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_civoid zonefs_update_stats(struct inode *inode, loff_t new_isize) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 15662306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 15762306a36Sopenharmony_ci loff_t old_isize = i_size_read(inode); 15862306a36Sopenharmony_ci loff_t nr_blocks; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (new_isize == old_isize) 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci spin_lock(&sbi->s_lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * This may be called for an update after an IO error. 16762306a36Sopenharmony_ci * So beware of the values seen. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci if (new_isize < old_isize) { 17062306a36Sopenharmony_ci nr_blocks = (old_isize - new_isize) >> sb->s_blocksize_bits; 17162306a36Sopenharmony_ci if (sbi->s_used_blocks > nr_blocks) 17262306a36Sopenharmony_ci sbi->s_used_blocks -= nr_blocks; 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci sbi->s_used_blocks = 0; 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci sbi->s_used_blocks += 17762306a36Sopenharmony_ci (new_isize - old_isize) >> sb->s_blocksize_bits; 17862306a36Sopenharmony_ci if (sbi->s_used_blocks > sbi->s_blocks) 17962306a36Sopenharmony_ci sbi->s_used_blocks = sbi->s_blocks; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci spin_unlock(&sbi->s_lock); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Check a zone condition. Return the amount of written (and still readable) 18762306a36Sopenharmony_ci * data in the zone. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic loff_t zonefs_check_zone_condition(struct super_block *sb, 19062306a36Sopenharmony_ci struct zonefs_zone *z, 19162306a36Sopenharmony_ci struct blk_zone *zone) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci switch (zone->cond) { 19462306a36Sopenharmony_ci case BLK_ZONE_COND_OFFLINE: 19562306a36Sopenharmony_ci zonefs_warn(sb, "Zone %llu: offline zone\n", 19662306a36Sopenharmony_ci z->z_sector); 19762306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_OFFLINE; 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci case BLK_ZONE_COND_READONLY: 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * The write pointer of read-only zones is invalid, so we cannot 20262306a36Sopenharmony_ci * determine the zone wpoffset (inode size). We thus keep the 20362306a36Sopenharmony_ci * zone wpoffset as is, which leads to an empty file 20462306a36Sopenharmony_ci * (wpoffset == 0) on mount. For a runtime error, this keeps 20562306a36Sopenharmony_ci * the inode size as it was when last updated so that the user 20662306a36Sopenharmony_ci * can recover data. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci zonefs_warn(sb, "Zone %llu: read-only zone\n", 20962306a36Sopenharmony_ci z->z_sector); 21062306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_READONLY; 21162306a36Sopenharmony_ci if (zonefs_zone_is_cnv(z)) 21262306a36Sopenharmony_ci return z->z_capacity; 21362306a36Sopenharmony_ci return z->z_wpoffset; 21462306a36Sopenharmony_ci case BLK_ZONE_COND_FULL: 21562306a36Sopenharmony_ci /* The write pointer of full zones is invalid. */ 21662306a36Sopenharmony_ci return z->z_capacity; 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci if (zonefs_zone_is_cnv(z)) 21962306a36Sopenharmony_ci return z->z_capacity; 22062306a36Sopenharmony_ci return (zone->wp - zone->start) << SECTOR_SHIFT; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * Check a zone condition and adjust its inode access permissions for 22662306a36Sopenharmony_ci * offline and readonly zones. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic void zonefs_inode_update_mode(struct inode *inode) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_OFFLINE) { 23362306a36Sopenharmony_ci /* Offline zones cannot be read nor written */ 23462306a36Sopenharmony_ci inode->i_flags |= S_IMMUTABLE; 23562306a36Sopenharmony_ci inode->i_mode &= ~0777; 23662306a36Sopenharmony_ci } else if (z->z_flags & ZONEFS_ZONE_READONLY) { 23762306a36Sopenharmony_ci /* Readonly zones cannot be written */ 23862306a36Sopenharmony_ci inode->i_flags |= S_IMMUTABLE; 23962306a36Sopenharmony_ci if (z->z_flags & ZONEFS_ZONE_INIT_MODE) 24062306a36Sopenharmony_ci inode->i_mode &= ~0777; 24162306a36Sopenharmony_ci else 24262306a36Sopenharmony_ci inode->i_mode &= ~0222; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci z->z_flags &= ~ZONEFS_ZONE_INIT_MODE; 24662306a36Sopenharmony_ci z->z_mode = inode->i_mode; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, 25062306a36Sopenharmony_ci void *data) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct blk_zone *z = data; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci *z = *zone; 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void zonefs_handle_io_error(struct inode *inode, struct blk_zone *zone, 25962306a36Sopenharmony_ci bool write) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 26262306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 26362306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 26462306a36Sopenharmony_ci loff_t isize, data_size; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * Check the zone condition: if the zone is not "bad" (offline or 26862306a36Sopenharmony_ci * read-only), read errors are simply signaled to the IO issuer as long 26962306a36Sopenharmony_ci * as there is no inconsistency between the inode size and the amount of 27062306a36Sopenharmony_ci * data writen in the zone (data_size). 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci data_size = zonefs_check_zone_condition(sb, z, zone); 27362306a36Sopenharmony_ci isize = i_size_read(inode); 27462306a36Sopenharmony_ci if (!(z->z_flags & (ZONEFS_ZONE_READONLY | ZONEFS_ZONE_OFFLINE)) && 27562306a36Sopenharmony_ci !write && isize == data_size) 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * At this point, we detected either a bad zone or an inconsistency 28062306a36Sopenharmony_ci * between the inode size and the amount of data written in the zone. 28162306a36Sopenharmony_ci * For the latter case, the cause may be a write IO error or an external 28262306a36Sopenharmony_ci * action on the device. Two error patterns exist: 28362306a36Sopenharmony_ci * 1) The inode size is lower than the amount of data in the zone: 28462306a36Sopenharmony_ci * a write operation partially failed and data was writen at the end 28562306a36Sopenharmony_ci * of the file. This can happen in the case of a large direct IO 28662306a36Sopenharmony_ci * needing several BIOs and/or write requests to be processed. 28762306a36Sopenharmony_ci * 2) The inode size is larger than the amount of data in the zone: 28862306a36Sopenharmony_ci * this can happen with a deferred write error with the use of the 28962306a36Sopenharmony_ci * device side write cache after getting successful write IO 29062306a36Sopenharmony_ci * completions. Other possibilities are (a) an external corruption, 29162306a36Sopenharmony_ci * e.g. an application reset the zone directly, or (b) the device 29262306a36Sopenharmony_ci * has a serious problem (e.g. firmware bug). 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * In all cases, warn about inode size inconsistency and handle the 29562306a36Sopenharmony_ci * IO error according to the zone condition and to the mount options. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (isize != data_size) 29862306a36Sopenharmony_ci zonefs_warn(sb, 29962306a36Sopenharmony_ci "inode %lu: invalid size %lld (should be %lld)\n", 30062306a36Sopenharmony_ci inode->i_ino, isize, data_size); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * First handle bad zones signaled by hardware. The mount options 30462306a36Sopenharmony_ci * errors=zone-ro and errors=zone-offline result in changing the 30562306a36Sopenharmony_ci * zone condition to read-only and offline respectively, as if the 30662306a36Sopenharmony_ci * condition was signaled by the hardware. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci if ((z->z_flags & ZONEFS_ZONE_OFFLINE) || 30962306a36Sopenharmony_ci (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL)) { 31062306a36Sopenharmony_ci zonefs_warn(sb, "inode %lu: read/write access disabled\n", 31162306a36Sopenharmony_ci inode->i_ino); 31262306a36Sopenharmony_ci if (!(z->z_flags & ZONEFS_ZONE_OFFLINE)) 31362306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_OFFLINE; 31462306a36Sopenharmony_ci zonefs_inode_update_mode(inode); 31562306a36Sopenharmony_ci data_size = 0; 31662306a36Sopenharmony_ci } else if ((z->z_flags & ZONEFS_ZONE_READONLY) || 31762306a36Sopenharmony_ci (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO)) { 31862306a36Sopenharmony_ci zonefs_warn(sb, "inode %lu: write access disabled\n", 31962306a36Sopenharmony_ci inode->i_ino); 32062306a36Sopenharmony_ci if (!(z->z_flags & ZONEFS_ZONE_READONLY)) 32162306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_READONLY; 32262306a36Sopenharmony_ci zonefs_inode_update_mode(inode); 32362306a36Sopenharmony_ci data_size = isize; 32462306a36Sopenharmony_ci } else if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO && 32562306a36Sopenharmony_ci data_size > isize) { 32662306a36Sopenharmony_ci /* Do not expose garbage data */ 32762306a36Sopenharmony_ci data_size = isize; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * If the filesystem is mounted with the explicit-open mount option, we 33262306a36Sopenharmony_ci * need to clear the ZONEFS_ZONE_OPEN flag if the zone transitioned to 33362306a36Sopenharmony_ci * the read-only or offline condition, to avoid attempting an explicit 33462306a36Sopenharmony_ci * close of the zone when the inode file is closed. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci if ((sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) && 33762306a36Sopenharmony_ci (z->z_flags & (ZONEFS_ZONE_READONLY | ZONEFS_ZONE_OFFLINE))) 33862306a36Sopenharmony_ci z->z_flags &= ~ZONEFS_ZONE_OPEN; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * If error=remount-ro was specified, any error result in remounting 34262306a36Sopenharmony_ci * the volume as read-only. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci if ((sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO) && !sb_rdonly(sb)) { 34562306a36Sopenharmony_ci zonefs_warn(sb, "remounting filesystem read-only\n"); 34662306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * Update block usage stats and the inode size to prevent access to 35162306a36Sopenharmony_ci * invalid data. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci zonefs_update_stats(inode, data_size); 35462306a36Sopenharmony_ci zonefs_i_size_write(inode, data_size); 35562306a36Sopenharmony_ci z->z_wpoffset = data_size; 35662306a36Sopenharmony_ci zonefs_inode_account_active(inode); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * When an file IO error occurs, check the file zone to see if there is a change 36162306a36Sopenharmony_ci * in the zone condition (e.g. offline or read-only). For a failed write to a 36262306a36Sopenharmony_ci * sequential zone, the zone write pointer position must also be checked to 36362306a36Sopenharmony_ci * eventually correct the file size and zonefs inode write pointer offset 36462306a36Sopenharmony_ci * (which can be out of sync with the drive due to partial write failures). 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_civoid __zonefs_io_error(struct inode *inode, bool write) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 36962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 37062306a36Sopenharmony_ci unsigned int noio_flag; 37162306a36Sopenharmony_ci struct blk_zone zone; 37262306a36Sopenharmony_ci int ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * Conventional zone have no write pointer and cannot become read-only 37662306a36Sopenharmony_ci * or offline. So simply fake a report for a single or aggregated zone 37762306a36Sopenharmony_ci * and let zonefs_handle_io_error() correct the zone inode information 37862306a36Sopenharmony_ci * according to the mount options. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci if (!zonefs_zone_is_seq(z)) { 38162306a36Sopenharmony_ci zone.start = z->z_sector; 38262306a36Sopenharmony_ci zone.len = z->z_size >> SECTOR_SHIFT; 38362306a36Sopenharmony_ci zone.wp = zone.start + zone.len; 38462306a36Sopenharmony_ci zone.type = BLK_ZONE_TYPE_CONVENTIONAL; 38562306a36Sopenharmony_ci zone.cond = BLK_ZONE_COND_NOT_WP; 38662306a36Sopenharmony_ci zone.capacity = zone.len; 38762306a36Sopenharmony_ci goto handle_io_error; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * Memory allocations in blkdev_report_zones() can trigger a memory 39262306a36Sopenharmony_ci * reclaim which may in turn cause a recursion into zonefs as well as 39362306a36Sopenharmony_ci * struct request allocations for the same device. The former case may 39462306a36Sopenharmony_ci * end up in a deadlock on the inode truncate mutex, while the latter 39562306a36Sopenharmony_ci * may prevent IO forward progress. Executing the report zones under 39662306a36Sopenharmony_ci * the GFP_NOIO context avoids both problems. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci noio_flag = memalloc_noio_save(); 39962306a36Sopenharmony_ci ret = blkdev_report_zones(sb->s_bdev, z->z_sector, 1, 40062306a36Sopenharmony_ci zonefs_io_error_cb, &zone); 40162306a36Sopenharmony_ci memalloc_noio_restore(noio_flag); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (ret != 1) { 40462306a36Sopenharmony_ci zonefs_err(sb, "Get inode %lu zone information failed %d\n", 40562306a36Sopenharmony_ci inode->i_ino, ret); 40662306a36Sopenharmony_ci zonefs_warn(sb, "remounting filesystem read-only\n"); 40762306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 40862306a36Sopenharmony_ci return; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cihandle_io_error: 41262306a36Sopenharmony_ci zonefs_handle_io_error(inode, &zone, write); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic struct kmem_cache *zonefs_inode_cachep; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic struct inode *zonefs_alloc_inode(struct super_block *sb) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct zonefs_inode_info *zi; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci zi = alloc_inode_sb(sb, zonefs_inode_cachep, GFP_KERNEL); 42262306a36Sopenharmony_ci if (!zi) 42362306a36Sopenharmony_ci return NULL; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci inode_init_once(&zi->i_vnode); 42662306a36Sopenharmony_ci mutex_init(&zi->i_truncate_mutex); 42762306a36Sopenharmony_ci zi->i_wr_refcnt = 0; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return &zi->i_vnode; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void zonefs_free_inode(struct inode *inode) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci kmem_cache_free(zonefs_inode_cachep, ZONEFS_I(inode)); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/* 43862306a36Sopenharmony_ci * File system stat. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_cistatic int zonefs_statfs(struct dentry *dentry, struct kstatfs *buf) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 44362306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 44462306a36Sopenharmony_ci enum zonefs_ztype t; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci buf->f_type = ZONEFS_MAGIC; 44762306a36Sopenharmony_ci buf->f_bsize = sb->s_blocksize; 44862306a36Sopenharmony_ci buf->f_namelen = ZONEFS_NAME_MAX; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci spin_lock(&sbi->s_lock); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci buf->f_blocks = sbi->s_blocks; 45362306a36Sopenharmony_ci if (WARN_ON(sbi->s_used_blocks > sbi->s_blocks)) 45462306a36Sopenharmony_ci buf->f_bfree = 0; 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci buf->f_bfree = buf->f_blocks - sbi->s_used_blocks; 45762306a36Sopenharmony_ci buf->f_bavail = buf->f_bfree; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci for (t = 0; t < ZONEFS_ZTYPE_MAX; t++) { 46062306a36Sopenharmony_ci if (sbi->s_zgroup[t].g_nr_zones) 46162306a36Sopenharmony_ci buf->f_files += sbi->s_zgroup[t].g_nr_zones + 1; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci buf->f_ffree = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci spin_unlock(&sbi->s_lock); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci buf->f_fsid = uuid_to_fsid(sbi->s_uuid.b); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cienum { 47362306a36Sopenharmony_ci Opt_errors_ro, Opt_errors_zro, Opt_errors_zol, Opt_errors_repair, 47462306a36Sopenharmony_ci Opt_explicit_open, Opt_err, 47562306a36Sopenharmony_ci}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic const match_table_t tokens = { 47862306a36Sopenharmony_ci { Opt_errors_ro, "errors=remount-ro"}, 47962306a36Sopenharmony_ci { Opt_errors_zro, "errors=zone-ro"}, 48062306a36Sopenharmony_ci { Opt_errors_zol, "errors=zone-offline"}, 48162306a36Sopenharmony_ci { Opt_errors_repair, "errors=repair"}, 48262306a36Sopenharmony_ci { Opt_explicit_open, "explicit-open" }, 48362306a36Sopenharmony_ci { Opt_err, NULL} 48462306a36Sopenharmony_ci}; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int zonefs_parse_options(struct super_block *sb, char *options) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 48962306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 49062306a36Sopenharmony_ci char *p; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (!options) 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci while ((p = strsep(&options, ",")) != NULL) { 49662306a36Sopenharmony_ci int token; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (!*p) 49962306a36Sopenharmony_ci continue; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci token = match_token(p, tokens, args); 50262306a36Sopenharmony_ci switch (token) { 50362306a36Sopenharmony_ci case Opt_errors_ro: 50462306a36Sopenharmony_ci sbi->s_mount_opts &= ~ZONEFS_MNTOPT_ERRORS_MASK; 50562306a36Sopenharmony_ci sbi->s_mount_opts |= ZONEFS_MNTOPT_ERRORS_RO; 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci case Opt_errors_zro: 50862306a36Sopenharmony_ci sbi->s_mount_opts &= ~ZONEFS_MNTOPT_ERRORS_MASK; 50962306a36Sopenharmony_ci sbi->s_mount_opts |= ZONEFS_MNTOPT_ERRORS_ZRO; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci case Opt_errors_zol: 51262306a36Sopenharmony_ci sbi->s_mount_opts &= ~ZONEFS_MNTOPT_ERRORS_MASK; 51362306a36Sopenharmony_ci sbi->s_mount_opts |= ZONEFS_MNTOPT_ERRORS_ZOL; 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci case Opt_errors_repair: 51662306a36Sopenharmony_ci sbi->s_mount_opts &= ~ZONEFS_MNTOPT_ERRORS_MASK; 51762306a36Sopenharmony_ci sbi->s_mount_opts |= ZONEFS_MNTOPT_ERRORS_REPAIR; 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci case Opt_explicit_open: 52062306a36Sopenharmony_ci sbi->s_mount_opts |= ZONEFS_MNTOPT_EXPLICIT_OPEN; 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci default: 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int zonefs_show_options(struct seq_file *seq, struct dentry *root) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(root->d_sb); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO) 53562306a36Sopenharmony_ci seq_puts(seq, ",errors=remount-ro"); 53662306a36Sopenharmony_ci if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) 53762306a36Sopenharmony_ci seq_puts(seq, ",errors=zone-ro"); 53862306a36Sopenharmony_ci if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL) 53962306a36Sopenharmony_ci seq_puts(seq, ",errors=zone-offline"); 54062306a36Sopenharmony_ci if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_REPAIR) 54162306a36Sopenharmony_ci seq_puts(seq, ",errors=repair"); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int zonefs_remount(struct super_block *sb, int *flags, char *data) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci sync_filesystem(sb); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return zonefs_parse_options(sb, data); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int zonefs_inode_setattr(struct mnt_idmap *idmap, 55462306a36Sopenharmony_ci struct dentry *dentry, struct iattr *iattr) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 55762306a36Sopenharmony_ci int ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (unlikely(IS_IMMUTABLE(inode))) 56062306a36Sopenharmony_ci return -EPERM; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ret = setattr_prepare(&nop_mnt_idmap, dentry, iattr); 56362306a36Sopenharmony_ci if (ret) 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * Since files and directories cannot be created nor deleted, do not 56862306a36Sopenharmony_ci * allow setting any write attributes on the sub-directories grouping 56962306a36Sopenharmony_ci * files by zone type. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci if ((iattr->ia_valid & ATTR_MODE) && S_ISDIR(inode->i_mode) && 57262306a36Sopenharmony_ci (iattr->ia_mode & 0222)) 57362306a36Sopenharmony_ci return -EPERM; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (((iattr->ia_valid & ATTR_UID) && 57662306a36Sopenharmony_ci !uid_eq(iattr->ia_uid, inode->i_uid)) || 57762306a36Sopenharmony_ci ((iattr->ia_valid & ATTR_GID) && 57862306a36Sopenharmony_ci !gid_eq(iattr->ia_gid, inode->i_gid))) { 57962306a36Sopenharmony_ci ret = dquot_transfer(&nop_mnt_idmap, inode, iattr); 58062306a36Sopenharmony_ci if (ret) 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_SIZE) { 58562306a36Sopenharmony_ci ret = zonefs_file_truncate(inode, iattr->ia_size); 58662306a36Sopenharmony_ci if (ret) 58762306a36Sopenharmony_ci return ret; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, iattr); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 59362306a36Sopenharmony_ci struct zonefs_zone *z = zonefs_inode_zone(inode); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci z->z_mode = inode->i_mode; 59662306a36Sopenharmony_ci z->z_uid = inode->i_uid; 59762306a36Sopenharmony_ci z->z_gid = inode->i_gid; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic const struct inode_operations zonefs_file_inode_operations = { 60462306a36Sopenharmony_ci .setattr = zonefs_inode_setattr, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic long zonefs_fname_to_fno(const struct qstr *fname) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci const char *name = fname->name; 61062306a36Sopenharmony_ci unsigned int len = fname->len; 61162306a36Sopenharmony_ci long fno = 0, shift = 1; 61262306a36Sopenharmony_ci const char *rname; 61362306a36Sopenharmony_ci char c = *name; 61462306a36Sopenharmony_ci unsigned int i; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * File names are always a base-10 number string without any 61862306a36Sopenharmony_ci * leading 0s. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci if (!isdigit(c)) 62162306a36Sopenharmony_ci return -ENOENT; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (len > 1 && c == '0') 62462306a36Sopenharmony_ci return -ENOENT; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (len == 1) 62762306a36Sopenharmony_ci return c - '0'; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci for (i = 0, rname = name + len - 1; i < len; i++, rname--) { 63062306a36Sopenharmony_ci c = *rname; 63162306a36Sopenharmony_ci if (!isdigit(c)) 63262306a36Sopenharmony_ci return -ENOENT; 63362306a36Sopenharmony_ci fno += (c - '0') * shift; 63462306a36Sopenharmony_ci shift *= 10; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return fno; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic struct inode *zonefs_get_file_inode(struct inode *dir, 64162306a36Sopenharmony_ci struct dentry *dentry) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct zonefs_zone_group *zgroup = dir->i_private; 64462306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 64562306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 64662306a36Sopenharmony_ci struct zonefs_zone *z; 64762306a36Sopenharmony_ci struct inode *inode; 64862306a36Sopenharmony_ci ino_t ino; 64962306a36Sopenharmony_ci long fno; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Get the file number from the file name */ 65262306a36Sopenharmony_ci fno = zonefs_fname_to_fno(&dentry->d_name); 65362306a36Sopenharmony_ci if (fno < 0) 65462306a36Sopenharmony_ci return ERR_PTR(fno); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!zgroup->g_nr_zones || fno >= zgroup->g_nr_zones) 65762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci z = &zgroup->g_zones[fno]; 66062306a36Sopenharmony_ci ino = z->z_sector >> sbi->s_zone_sectors_shift; 66162306a36Sopenharmony_ci inode = iget_locked(sb, ino); 66262306a36Sopenharmony_ci if (!inode) 66362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 66462306a36Sopenharmony_ci if (!(inode->i_state & I_NEW)) { 66562306a36Sopenharmony_ci WARN_ON_ONCE(inode->i_private != z); 66662306a36Sopenharmony_ci return inode; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci inode->i_ino = ino; 67062306a36Sopenharmony_ci inode->i_mode = z->z_mode; 67162306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_to_ts(inode, 67262306a36Sopenharmony_ci inode_get_ctime(dir)); 67362306a36Sopenharmony_ci inode->i_uid = z->z_uid; 67462306a36Sopenharmony_ci inode->i_gid = z->z_gid; 67562306a36Sopenharmony_ci inode->i_size = z->z_wpoffset; 67662306a36Sopenharmony_ci inode->i_blocks = z->z_capacity >> SECTOR_SHIFT; 67762306a36Sopenharmony_ci inode->i_private = z; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci inode->i_op = &zonefs_file_inode_operations; 68062306a36Sopenharmony_ci inode->i_fop = &zonefs_file_operations; 68162306a36Sopenharmony_ci inode->i_mapping->a_ops = &zonefs_file_aops; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Update the inode access rights depending on the zone condition */ 68462306a36Sopenharmony_ci zonefs_inode_update_mode(inode); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci unlock_new_inode(inode); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return inode; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic struct inode *zonefs_get_zgroup_inode(struct super_block *sb, 69262306a36Sopenharmony_ci enum zonefs_ztype ztype) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct inode *root = d_inode(sb->s_root); 69562306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 69662306a36Sopenharmony_ci struct inode *inode; 69762306a36Sopenharmony_ci ino_t ino = bdev_nr_zones(sb->s_bdev) + ztype + 1; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci inode = iget_locked(sb, ino); 70062306a36Sopenharmony_ci if (!inode) 70162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 70262306a36Sopenharmony_ci if (!(inode->i_state & I_NEW)) 70362306a36Sopenharmony_ci return inode; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci inode->i_ino = ino; 70662306a36Sopenharmony_ci inode_init_owner(&nop_mnt_idmap, inode, root, S_IFDIR | 0555); 70762306a36Sopenharmony_ci inode->i_size = sbi->s_zgroup[ztype].g_nr_zones; 70862306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_to_ts(inode, 70962306a36Sopenharmony_ci inode_get_ctime(root)); 71062306a36Sopenharmony_ci inode->i_private = &sbi->s_zgroup[ztype]; 71162306a36Sopenharmony_ci set_nlink(inode, 2); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci inode->i_op = &zonefs_dir_inode_operations; 71462306a36Sopenharmony_ci inode->i_fop = &zonefs_dir_operations; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci unlock_new_inode(inode); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return inode; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic struct inode *zonefs_get_dir_inode(struct inode *dir, 72362306a36Sopenharmony_ci struct dentry *dentry) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 72662306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 72762306a36Sopenharmony_ci const char *name = dentry->d_name.name; 72862306a36Sopenharmony_ci enum zonefs_ztype ztype; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci * We only need to check for the "seq" directory and 73262306a36Sopenharmony_ci * the "cnv" directory if we have conventional zones. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (dentry->d_name.len != 3) 73562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { 73862306a36Sopenharmony_ci if (sbi->s_zgroup[ztype].g_nr_zones && 73962306a36Sopenharmony_ci memcmp(name, zonefs_zgroup_name(ztype), 3) == 0) 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci if (ztype == ZONEFS_ZTYPE_MAX) 74362306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return zonefs_get_zgroup_inode(sb, ztype); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic struct dentry *zonefs_lookup(struct inode *dir, struct dentry *dentry, 74962306a36Sopenharmony_ci unsigned int flags) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct inode *inode; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (dentry->d_name.len > ZONEFS_NAME_MAX) 75462306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (dir == d_inode(dir->i_sb->s_root)) 75762306a36Sopenharmony_ci inode = zonefs_get_dir_inode(dir, dentry); 75862306a36Sopenharmony_ci else 75962306a36Sopenharmony_ci inode = zonefs_get_file_inode(dir, dentry); 76062306a36Sopenharmony_ci if (IS_ERR(inode)) 76162306a36Sopenharmony_ci return ERR_CAST(inode); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic int zonefs_readdir_root(struct file *file, struct dir_context *ctx) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 76962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 77062306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 77162306a36Sopenharmony_ci enum zonefs_ztype ztype = ZONEFS_ZTYPE_CNV; 77262306a36Sopenharmony_ci ino_t base_ino = bdev_nr_zones(sb->s_bdev) + 1; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (ctx->pos >= inode->i_size) 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (ctx->pos == 2) { 78162306a36Sopenharmony_ci if (!sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones) 78262306a36Sopenharmony_ci ztype = ZONEFS_ZTYPE_SEQ; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (!dir_emit(ctx, zonefs_zgroup_name(ztype), 3, 78562306a36Sopenharmony_ci base_ino + ztype, DT_DIR)) 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci ctx->pos++; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (ctx->pos == 3 && ztype != ZONEFS_ZTYPE_SEQ) { 79162306a36Sopenharmony_ci ztype = ZONEFS_ZTYPE_SEQ; 79262306a36Sopenharmony_ci if (!dir_emit(ctx, zonefs_zgroup_name(ztype), 3, 79362306a36Sopenharmony_ci base_ino + ztype, DT_DIR)) 79462306a36Sopenharmony_ci return 0; 79562306a36Sopenharmony_ci ctx->pos++; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic int zonefs_readdir_zgroup(struct file *file, 80262306a36Sopenharmony_ci struct dir_context *ctx) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct inode *inode = file_inode(file); 80562306a36Sopenharmony_ci struct zonefs_zone_group *zgroup = inode->i_private; 80662306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 80762306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 80862306a36Sopenharmony_ci struct zonefs_zone *z; 80962306a36Sopenharmony_ci int fname_len; 81062306a36Sopenharmony_ci char *fname; 81162306a36Sopenharmony_ci ino_t ino; 81262306a36Sopenharmony_ci int f; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* 81562306a36Sopenharmony_ci * The size of zone group directories is equal to the number 81662306a36Sopenharmony_ci * of zone files in the group and does note include the "." and 81762306a36Sopenharmony_ci * ".." entries. Hence the "+ 2" here. 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_ci if (ctx->pos >= inode->i_size + 2) 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci fname = kmalloc(ZONEFS_NAME_MAX, GFP_KERNEL); 82662306a36Sopenharmony_ci if (!fname) 82762306a36Sopenharmony_ci return -ENOMEM; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci for (f = ctx->pos - 2; f < zgroup->g_nr_zones; f++) { 83062306a36Sopenharmony_ci z = &zgroup->g_zones[f]; 83162306a36Sopenharmony_ci ino = z->z_sector >> sbi->s_zone_sectors_shift; 83262306a36Sopenharmony_ci fname_len = snprintf(fname, ZONEFS_NAME_MAX - 1, "%u", f); 83362306a36Sopenharmony_ci if (!dir_emit(ctx, fname, fname_len, ino, DT_REG)) 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci ctx->pos++; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci kfree(fname); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int zonefs_readdir(struct file *file, struct dir_context *ctx) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (inode == d_inode(inode->i_sb->s_root)) 84862306a36Sopenharmony_ci return zonefs_readdir_root(file, ctx); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return zonefs_readdir_zgroup(file, ctx); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ciconst struct inode_operations zonefs_dir_inode_operations = { 85462306a36Sopenharmony_ci .lookup = zonefs_lookup, 85562306a36Sopenharmony_ci .setattr = zonefs_inode_setattr, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ciconst struct file_operations zonefs_dir_operations = { 85962306a36Sopenharmony_ci .llseek = generic_file_llseek, 86062306a36Sopenharmony_ci .read = generic_read_dir, 86162306a36Sopenharmony_ci .iterate_shared = zonefs_readdir, 86262306a36Sopenharmony_ci}; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistruct zonefs_zone_data { 86562306a36Sopenharmony_ci struct super_block *sb; 86662306a36Sopenharmony_ci unsigned int nr_zones[ZONEFS_ZTYPE_MAX]; 86762306a36Sopenharmony_ci sector_t cnv_zone_start; 86862306a36Sopenharmony_ci struct blk_zone *zones; 86962306a36Sopenharmony_ci}; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int zonefs_get_zone_info_cb(struct blk_zone *zone, unsigned int idx, 87262306a36Sopenharmony_ci void *data) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct zonefs_zone_data *zd = data; 87562306a36Sopenharmony_ci struct super_block *sb = zd->sb; 87662306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* 87962306a36Sopenharmony_ci * We do not care about the first zone: it contains the super block 88062306a36Sopenharmony_ci * and not exposed as a file. 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_ci if (!idx) 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * Count the number of zones that will be exposed as files. 88762306a36Sopenharmony_ci * For sequential zones, we always have as many files as zones. 88862306a36Sopenharmony_ci * FOr conventional zones, the number of files depends on if we have 88962306a36Sopenharmony_ci * conventional zones aggregation enabled. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci switch (zone->type) { 89262306a36Sopenharmony_ci case BLK_ZONE_TYPE_CONVENTIONAL: 89362306a36Sopenharmony_ci if (sbi->s_features & ZONEFS_F_AGGRCNV) { 89462306a36Sopenharmony_ci /* One file per set of contiguous conventional zones */ 89562306a36Sopenharmony_ci if (!(sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones) || 89662306a36Sopenharmony_ci zone->start != zd->cnv_zone_start) 89762306a36Sopenharmony_ci sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones++; 89862306a36Sopenharmony_ci zd->cnv_zone_start = zone->start + zone->len; 89962306a36Sopenharmony_ci } else { 90062306a36Sopenharmony_ci /* One file per zone */ 90162306a36Sopenharmony_ci sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones++; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci case BLK_ZONE_TYPE_SEQWRITE_REQ: 90562306a36Sopenharmony_ci case BLK_ZONE_TYPE_SEQWRITE_PREF: 90662306a36Sopenharmony_ci sbi->s_zgroup[ZONEFS_ZTYPE_SEQ].g_nr_zones++; 90762306a36Sopenharmony_ci break; 90862306a36Sopenharmony_ci default: 90962306a36Sopenharmony_ci zonefs_err(zd->sb, "Unsupported zone type 0x%x\n", 91062306a36Sopenharmony_ci zone->type); 91162306a36Sopenharmony_ci return -EIO; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci memcpy(&zd->zones[idx], zone, sizeof(struct blk_zone)); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic int zonefs_get_zone_info(struct zonefs_zone_data *zd) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci struct block_device *bdev = zd->sb->s_bdev; 92262306a36Sopenharmony_ci int ret; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci zd->zones = kvcalloc(bdev_nr_zones(bdev), sizeof(struct blk_zone), 92562306a36Sopenharmony_ci GFP_KERNEL); 92662306a36Sopenharmony_ci if (!zd->zones) 92762306a36Sopenharmony_ci return -ENOMEM; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* Get zones information from the device */ 93062306a36Sopenharmony_ci ret = blkdev_report_zones(bdev, 0, BLK_ALL_ZONES, 93162306a36Sopenharmony_ci zonefs_get_zone_info_cb, zd); 93262306a36Sopenharmony_ci if (ret < 0) { 93362306a36Sopenharmony_ci zonefs_err(zd->sb, "Zone report failed %d\n", ret); 93462306a36Sopenharmony_ci return ret; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (ret != bdev_nr_zones(bdev)) { 93862306a36Sopenharmony_ci zonefs_err(zd->sb, "Invalid zone report (%d/%u zones)\n", 93962306a36Sopenharmony_ci ret, bdev_nr_zones(bdev)); 94062306a36Sopenharmony_ci return -EIO; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic inline void zonefs_free_zone_info(struct zonefs_zone_data *zd) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci kvfree(zd->zones); 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci/* 95262306a36Sopenharmony_ci * Create a zone group and populate it with zone files. 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_cistatic int zonefs_init_zgroup(struct super_block *sb, 95562306a36Sopenharmony_ci struct zonefs_zone_data *zd, 95662306a36Sopenharmony_ci enum zonefs_ztype ztype) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 95962306a36Sopenharmony_ci struct zonefs_zone_group *zgroup = &sbi->s_zgroup[ztype]; 96062306a36Sopenharmony_ci struct blk_zone *zone, *next, *end; 96162306a36Sopenharmony_ci struct zonefs_zone *z; 96262306a36Sopenharmony_ci unsigned int n = 0; 96362306a36Sopenharmony_ci int ret; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* Allocate the zone group. If it is empty, we have nothing to do. */ 96662306a36Sopenharmony_ci if (!zgroup->g_nr_zones) 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci zgroup->g_zones = kvcalloc(zgroup->g_nr_zones, 97062306a36Sopenharmony_ci sizeof(struct zonefs_zone), GFP_KERNEL); 97162306a36Sopenharmony_ci if (!zgroup->g_zones) 97262306a36Sopenharmony_ci return -ENOMEM; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* 97562306a36Sopenharmony_ci * Initialize the zone groups using the device zone information. 97662306a36Sopenharmony_ci * We always skip the first zone as it contains the super block 97762306a36Sopenharmony_ci * and is not use to back a file. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ci end = zd->zones + bdev_nr_zones(sb->s_bdev); 98062306a36Sopenharmony_ci for (zone = &zd->zones[1]; zone < end; zone = next) { 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci next = zone + 1; 98362306a36Sopenharmony_ci if (zonefs_zone_type(zone) != ztype) 98462306a36Sopenharmony_ci continue; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (WARN_ON_ONCE(n >= zgroup->g_nr_zones)) 98762306a36Sopenharmony_ci return -EINVAL; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* 99062306a36Sopenharmony_ci * For conventional zones, contiguous zones can be aggregated 99162306a36Sopenharmony_ci * together to form larger files. Note that this overwrites the 99262306a36Sopenharmony_ci * length of the first zone of the set of contiguous zones 99362306a36Sopenharmony_ci * aggregated together. If one offline or read-only zone is 99462306a36Sopenharmony_ci * found, assume that all zones aggregated have the same 99562306a36Sopenharmony_ci * condition. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci if (ztype == ZONEFS_ZTYPE_CNV && 99862306a36Sopenharmony_ci (sbi->s_features & ZONEFS_F_AGGRCNV)) { 99962306a36Sopenharmony_ci for (; next < end; next++) { 100062306a36Sopenharmony_ci if (zonefs_zone_type(next) != ztype) 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci zone->len += next->len; 100362306a36Sopenharmony_ci zone->capacity += next->capacity; 100462306a36Sopenharmony_ci if (next->cond == BLK_ZONE_COND_READONLY && 100562306a36Sopenharmony_ci zone->cond != BLK_ZONE_COND_OFFLINE) 100662306a36Sopenharmony_ci zone->cond = BLK_ZONE_COND_READONLY; 100762306a36Sopenharmony_ci else if (next->cond == BLK_ZONE_COND_OFFLINE) 100862306a36Sopenharmony_ci zone->cond = BLK_ZONE_COND_OFFLINE; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci z = &zgroup->g_zones[n]; 101362306a36Sopenharmony_ci if (ztype == ZONEFS_ZTYPE_CNV) 101462306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_CNV; 101562306a36Sopenharmony_ci z->z_sector = zone->start; 101662306a36Sopenharmony_ci z->z_size = zone->len << SECTOR_SHIFT; 101762306a36Sopenharmony_ci if (z->z_size > bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT && 101862306a36Sopenharmony_ci !(sbi->s_features & ZONEFS_F_AGGRCNV)) { 101962306a36Sopenharmony_ci zonefs_err(sb, 102062306a36Sopenharmony_ci "Invalid zone size %llu (device zone sectors %llu)\n", 102162306a36Sopenharmony_ci z->z_size, 102262306a36Sopenharmony_ci bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT); 102362306a36Sopenharmony_ci return -EINVAL; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci z->z_capacity = min_t(loff_t, MAX_LFS_FILESIZE, 102762306a36Sopenharmony_ci zone->capacity << SECTOR_SHIFT); 102862306a36Sopenharmony_ci z->z_wpoffset = zonefs_check_zone_condition(sb, z, zone); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci z->z_mode = S_IFREG | sbi->s_perm; 103162306a36Sopenharmony_ci z->z_uid = sbi->s_uid; 103262306a36Sopenharmony_ci z->z_gid = sbi->s_gid; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * Let zonefs_inode_update_mode() know that we will need 103662306a36Sopenharmony_ci * special initialization of the inode mode the first time 103762306a36Sopenharmony_ci * it is accessed. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci z->z_flags |= ZONEFS_ZONE_INIT_MODE; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci sb->s_maxbytes = max(z->z_capacity, sb->s_maxbytes); 104262306a36Sopenharmony_ci sbi->s_blocks += z->z_capacity >> sb->s_blocksize_bits; 104362306a36Sopenharmony_ci sbi->s_used_blocks += z->z_wpoffset >> sb->s_blocksize_bits; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* 104662306a36Sopenharmony_ci * For sequential zones, make sure that any open zone is closed 104762306a36Sopenharmony_ci * first to ensure that the initial number of open zones is 0, 104862306a36Sopenharmony_ci * in sync with the open zone accounting done when the mount 104962306a36Sopenharmony_ci * option ZONEFS_MNTOPT_EXPLICIT_OPEN is used. 105062306a36Sopenharmony_ci */ 105162306a36Sopenharmony_ci if (ztype == ZONEFS_ZTYPE_SEQ && 105262306a36Sopenharmony_ci (zone->cond == BLK_ZONE_COND_IMP_OPEN || 105362306a36Sopenharmony_ci zone->cond == BLK_ZONE_COND_EXP_OPEN)) { 105462306a36Sopenharmony_ci ret = zonefs_zone_mgmt(sb, z, REQ_OP_ZONE_CLOSE); 105562306a36Sopenharmony_ci if (ret) 105662306a36Sopenharmony_ci return ret; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci zonefs_account_active(sb, z); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci n++; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (WARN_ON_ONCE(n != zgroup->g_nr_zones)) 106562306a36Sopenharmony_ci return -EINVAL; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci zonefs_info(sb, "Zone group \"%s\" has %u file%s\n", 106862306a36Sopenharmony_ci zonefs_zgroup_name(ztype), 106962306a36Sopenharmony_ci zgroup->g_nr_zones, 107062306a36Sopenharmony_ci zgroup->g_nr_zones > 1 ? "s" : ""); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return 0; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void zonefs_free_zgroups(struct super_block *sb) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 107862306a36Sopenharmony_ci enum zonefs_ztype ztype; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (!sbi) 108162306a36Sopenharmony_ci return; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { 108462306a36Sopenharmony_ci kvfree(sbi->s_zgroup[ztype].g_zones); 108562306a36Sopenharmony_ci sbi->s_zgroup[ztype].g_zones = NULL; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/* 109062306a36Sopenharmony_ci * Create a zone group and populate it with zone files. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_cistatic int zonefs_init_zgroups(struct super_block *sb) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct zonefs_zone_data zd; 109562306a36Sopenharmony_ci enum zonefs_ztype ztype; 109662306a36Sopenharmony_ci int ret; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* First get the device zone information */ 109962306a36Sopenharmony_ci memset(&zd, 0, sizeof(struct zonefs_zone_data)); 110062306a36Sopenharmony_ci zd.sb = sb; 110162306a36Sopenharmony_ci ret = zonefs_get_zone_info(&zd); 110262306a36Sopenharmony_ci if (ret) 110362306a36Sopenharmony_ci goto cleanup; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci /* Allocate and initialize the zone groups */ 110662306a36Sopenharmony_ci for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { 110762306a36Sopenharmony_ci ret = zonefs_init_zgroup(sb, &zd, ztype); 110862306a36Sopenharmony_ci if (ret) { 110962306a36Sopenharmony_ci zonefs_info(sb, 111062306a36Sopenharmony_ci "Zone group \"%s\" initialization failed\n", 111162306a36Sopenharmony_ci zonefs_zgroup_name(ztype)); 111262306a36Sopenharmony_ci break; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cicleanup: 111762306a36Sopenharmony_ci zonefs_free_zone_info(&zd); 111862306a36Sopenharmony_ci if (ret) 111962306a36Sopenharmony_ci zonefs_free_zgroups(sb); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci return ret; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci/* 112562306a36Sopenharmony_ci * Read super block information from the device. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_cistatic int zonefs_read_super(struct super_block *sb) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 113062306a36Sopenharmony_ci struct zonefs_super *super; 113162306a36Sopenharmony_ci u32 crc, stored_crc; 113262306a36Sopenharmony_ci struct page *page; 113362306a36Sopenharmony_ci struct bio_vec bio_vec; 113462306a36Sopenharmony_ci struct bio bio; 113562306a36Sopenharmony_ci int ret; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL); 113862306a36Sopenharmony_ci if (!page) 113962306a36Sopenharmony_ci return -ENOMEM; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci bio_init(&bio, sb->s_bdev, &bio_vec, 1, REQ_OP_READ); 114262306a36Sopenharmony_ci bio.bi_iter.bi_sector = 0; 114362306a36Sopenharmony_ci __bio_add_page(&bio, page, PAGE_SIZE, 0); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci ret = submit_bio_wait(&bio); 114662306a36Sopenharmony_ci if (ret) 114762306a36Sopenharmony_ci goto free_page; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci super = page_address(page); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci ret = -EINVAL; 115262306a36Sopenharmony_ci if (le32_to_cpu(super->s_magic) != ZONEFS_MAGIC) 115362306a36Sopenharmony_ci goto free_page; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci stored_crc = le32_to_cpu(super->s_crc); 115662306a36Sopenharmony_ci super->s_crc = 0; 115762306a36Sopenharmony_ci crc = crc32(~0U, (unsigned char *)super, sizeof(struct zonefs_super)); 115862306a36Sopenharmony_ci if (crc != stored_crc) { 115962306a36Sopenharmony_ci zonefs_err(sb, "Invalid checksum (Expected 0x%08x, got 0x%08x)", 116062306a36Sopenharmony_ci crc, stored_crc); 116162306a36Sopenharmony_ci goto free_page; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci sbi->s_features = le64_to_cpu(super->s_features); 116562306a36Sopenharmony_ci if (sbi->s_features & ~ZONEFS_F_DEFINED_FEATURES) { 116662306a36Sopenharmony_ci zonefs_err(sb, "Unknown features set 0x%llx\n", 116762306a36Sopenharmony_ci sbi->s_features); 116862306a36Sopenharmony_ci goto free_page; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (sbi->s_features & ZONEFS_F_UID) { 117262306a36Sopenharmony_ci sbi->s_uid = make_kuid(current_user_ns(), 117362306a36Sopenharmony_ci le32_to_cpu(super->s_uid)); 117462306a36Sopenharmony_ci if (!uid_valid(sbi->s_uid)) { 117562306a36Sopenharmony_ci zonefs_err(sb, "Invalid UID feature\n"); 117662306a36Sopenharmony_ci goto free_page; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (sbi->s_features & ZONEFS_F_GID) { 118162306a36Sopenharmony_ci sbi->s_gid = make_kgid(current_user_ns(), 118262306a36Sopenharmony_ci le32_to_cpu(super->s_gid)); 118362306a36Sopenharmony_ci if (!gid_valid(sbi->s_gid)) { 118462306a36Sopenharmony_ci zonefs_err(sb, "Invalid GID feature\n"); 118562306a36Sopenharmony_ci goto free_page; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (sbi->s_features & ZONEFS_F_PERM) 119062306a36Sopenharmony_ci sbi->s_perm = le32_to_cpu(super->s_perm); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (memchr_inv(super->s_reserved, 0, sizeof(super->s_reserved))) { 119362306a36Sopenharmony_ci zonefs_err(sb, "Reserved area is being used\n"); 119462306a36Sopenharmony_ci goto free_page; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci import_uuid(&sbi->s_uuid, super->s_uuid); 119862306a36Sopenharmony_ci ret = 0; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cifree_page: 120162306a36Sopenharmony_ci __free_page(page); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci return ret; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic const struct super_operations zonefs_sops = { 120762306a36Sopenharmony_ci .alloc_inode = zonefs_alloc_inode, 120862306a36Sopenharmony_ci .free_inode = zonefs_free_inode, 120962306a36Sopenharmony_ci .statfs = zonefs_statfs, 121062306a36Sopenharmony_ci .remount_fs = zonefs_remount, 121162306a36Sopenharmony_ci .show_options = zonefs_show_options, 121262306a36Sopenharmony_ci}; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic int zonefs_get_zgroup_inodes(struct super_block *sb) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 121762306a36Sopenharmony_ci struct inode *dir_inode; 121862306a36Sopenharmony_ci enum zonefs_ztype ztype; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { 122162306a36Sopenharmony_ci if (!sbi->s_zgroup[ztype].g_nr_zones) 122262306a36Sopenharmony_ci continue; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci dir_inode = zonefs_get_zgroup_inode(sb, ztype); 122562306a36Sopenharmony_ci if (IS_ERR(dir_inode)) 122662306a36Sopenharmony_ci return PTR_ERR(dir_inode); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci sbi->s_zgroup[ztype].g_inode = dir_inode; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci return 0; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic void zonefs_release_zgroup_inodes(struct super_block *sb) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 123762306a36Sopenharmony_ci enum zonefs_ztype ztype; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (!sbi) 124062306a36Sopenharmony_ci return; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { 124362306a36Sopenharmony_ci if (sbi->s_zgroup[ztype].g_inode) { 124462306a36Sopenharmony_ci iput(sbi->s_zgroup[ztype].g_inode); 124562306a36Sopenharmony_ci sbi->s_zgroup[ztype].g_inode = NULL; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/* 125162306a36Sopenharmony_ci * Check that the device is zoned. If it is, get the list of zones and create 125262306a36Sopenharmony_ci * sub-directories and files according to the device zone configuration and 125362306a36Sopenharmony_ci * format options. 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_cistatic int zonefs_fill_super(struct super_block *sb, void *data, int silent) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct zonefs_sb_info *sbi; 125862306a36Sopenharmony_ci struct inode *inode; 125962306a36Sopenharmony_ci enum zonefs_ztype ztype; 126062306a36Sopenharmony_ci int ret; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (!bdev_is_zoned(sb->s_bdev)) { 126362306a36Sopenharmony_ci zonefs_err(sb, "Not a zoned block device\n"); 126462306a36Sopenharmony_ci return -EINVAL; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* 126862306a36Sopenharmony_ci * Initialize super block information: the maximum file size is updated 126962306a36Sopenharmony_ci * when the zone files are created so that the format option 127062306a36Sopenharmony_ci * ZONEFS_F_AGGRCNV which increases the maximum file size of a file 127162306a36Sopenharmony_ci * beyond the zone size is taken into account. 127262306a36Sopenharmony_ci */ 127362306a36Sopenharmony_ci sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); 127462306a36Sopenharmony_ci if (!sbi) 127562306a36Sopenharmony_ci return -ENOMEM; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci spin_lock_init(&sbi->s_lock); 127862306a36Sopenharmony_ci sb->s_fs_info = sbi; 127962306a36Sopenharmony_ci sb->s_magic = ZONEFS_MAGIC; 128062306a36Sopenharmony_ci sb->s_maxbytes = 0; 128162306a36Sopenharmony_ci sb->s_op = &zonefs_sops; 128262306a36Sopenharmony_ci sb->s_time_gran = 1; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* 128562306a36Sopenharmony_ci * The block size is set to the device zone write granularity to ensure 128662306a36Sopenharmony_ci * that write operations are always aligned according to the device 128762306a36Sopenharmony_ci * interface constraints. 128862306a36Sopenharmony_ci */ 128962306a36Sopenharmony_ci sb_set_blocksize(sb, bdev_zone_write_granularity(sb->s_bdev)); 129062306a36Sopenharmony_ci sbi->s_zone_sectors_shift = ilog2(bdev_zone_sectors(sb->s_bdev)); 129162306a36Sopenharmony_ci sbi->s_uid = GLOBAL_ROOT_UID; 129262306a36Sopenharmony_ci sbi->s_gid = GLOBAL_ROOT_GID; 129362306a36Sopenharmony_ci sbi->s_perm = 0640; 129462306a36Sopenharmony_ci sbi->s_mount_opts = ZONEFS_MNTOPT_ERRORS_RO; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci atomic_set(&sbi->s_wro_seq_files, 0); 129762306a36Sopenharmony_ci sbi->s_max_wro_seq_files = bdev_max_open_zones(sb->s_bdev); 129862306a36Sopenharmony_ci atomic_set(&sbi->s_active_seq_files, 0); 129962306a36Sopenharmony_ci sbi->s_max_active_seq_files = bdev_max_active_zones(sb->s_bdev); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci ret = zonefs_read_super(sb); 130262306a36Sopenharmony_ci if (ret) 130362306a36Sopenharmony_ci return ret; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci ret = zonefs_parse_options(sb, data); 130662306a36Sopenharmony_ci if (ret) 130762306a36Sopenharmony_ci return ret; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci zonefs_info(sb, "Mounting %u zones", bdev_nr_zones(sb->s_bdev)); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (!sbi->s_max_wro_seq_files && 131262306a36Sopenharmony_ci !sbi->s_max_active_seq_files && 131362306a36Sopenharmony_ci sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) { 131462306a36Sopenharmony_ci zonefs_info(sb, 131562306a36Sopenharmony_ci "No open and active zone limits. Ignoring explicit_open mount option\n"); 131662306a36Sopenharmony_ci sbi->s_mount_opts &= ~ZONEFS_MNTOPT_EXPLICIT_OPEN; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* Initialize the zone groups */ 132062306a36Sopenharmony_ci ret = zonefs_init_zgroups(sb); 132162306a36Sopenharmony_ci if (ret) 132262306a36Sopenharmony_ci goto cleanup; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* Create the root directory inode */ 132562306a36Sopenharmony_ci ret = -ENOMEM; 132662306a36Sopenharmony_ci inode = new_inode(sb); 132762306a36Sopenharmony_ci if (!inode) 132862306a36Sopenharmony_ci goto cleanup; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci inode->i_ino = bdev_nr_zones(sb->s_bdev); 133162306a36Sopenharmony_ci inode->i_mode = S_IFDIR | 0555; 133262306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode); 133362306a36Sopenharmony_ci inode->i_op = &zonefs_dir_inode_operations; 133462306a36Sopenharmony_ci inode->i_fop = &zonefs_dir_operations; 133562306a36Sopenharmony_ci inode->i_size = 2; 133662306a36Sopenharmony_ci set_nlink(inode, 2); 133762306a36Sopenharmony_ci for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { 133862306a36Sopenharmony_ci if (sbi->s_zgroup[ztype].g_nr_zones) { 133962306a36Sopenharmony_ci inc_nlink(inode); 134062306a36Sopenharmony_ci inode->i_size++; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci sb->s_root = d_make_root(inode); 134562306a36Sopenharmony_ci if (!sb->s_root) 134662306a36Sopenharmony_ci goto cleanup; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* 134962306a36Sopenharmony_ci * Take a reference on the zone groups directory inodes 135062306a36Sopenharmony_ci * to keep them in the inode cache. 135162306a36Sopenharmony_ci */ 135262306a36Sopenharmony_ci ret = zonefs_get_zgroup_inodes(sb); 135362306a36Sopenharmony_ci if (ret) 135462306a36Sopenharmony_ci goto cleanup; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci ret = zonefs_sysfs_register(sb); 135762306a36Sopenharmony_ci if (ret) 135862306a36Sopenharmony_ci goto cleanup; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cicleanup: 136362306a36Sopenharmony_ci zonefs_release_zgroup_inodes(sb); 136462306a36Sopenharmony_ci zonefs_free_zgroups(sb); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci return ret; 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cistatic struct dentry *zonefs_mount(struct file_system_type *fs_type, 137062306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci return mount_bdev(fs_type, flags, dev_name, data, zonefs_fill_super); 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic void zonefs_kill_super(struct super_block *sb) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci struct zonefs_sb_info *sbi = ZONEFS_SB(sb); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci /* Release the reference on the zone group directory inodes */ 138062306a36Sopenharmony_ci zonefs_release_zgroup_inodes(sb); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci kill_block_super(sb); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci zonefs_sysfs_unregister(sb); 138562306a36Sopenharmony_ci zonefs_free_zgroups(sb); 138662306a36Sopenharmony_ci kfree(sbi); 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci/* 139062306a36Sopenharmony_ci * File system definition and registration. 139162306a36Sopenharmony_ci */ 139262306a36Sopenharmony_cistatic struct file_system_type zonefs_type = { 139362306a36Sopenharmony_ci .owner = THIS_MODULE, 139462306a36Sopenharmony_ci .name = "zonefs", 139562306a36Sopenharmony_ci .mount = zonefs_mount, 139662306a36Sopenharmony_ci .kill_sb = zonefs_kill_super, 139762306a36Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 139862306a36Sopenharmony_ci}; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic int __init zonefs_init_inodecache(void) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci zonefs_inode_cachep = kmem_cache_create("zonefs_inode_cache", 140362306a36Sopenharmony_ci sizeof(struct zonefs_inode_info), 0, 140462306a36Sopenharmony_ci (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT), 140562306a36Sopenharmony_ci NULL); 140662306a36Sopenharmony_ci if (zonefs_inode_cachep == NULL) 140762306a36Sopenharmony_ci return -ENOMEM; 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic void zonefs_destroy_inodecache(void) 141262306a36Sopenharmony_ci{ 141362306a36Sopenharmony_ci /* 141462306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 141562306a36Sopenharmony_ci * destroy the inode cache. 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_ci rcu_barrier(); 141862306a36Sopenharmony_ci kmem_cache_destroy(zonefs_inode_cachep); 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic int __init zonefs_init(void) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci int ret; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct zonefs_super) != ZONEFS_SUPER_SIZE); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci ret = zonefs_init_inodecache(); 142862306a36Sopenharmony_ci if (ret) 142962306a36Sopenharmony_ci return ret; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci ret = zonefs_sysfs_init(); 143262306a36Sopenharmony_ci if (ret) 143362306a36Sopenharmony_ci goto destroy_inodecache; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci ret = register_filesystem(&zonefs_type); 143662306a36Sopenharmony_ci if (ret) 143762306a36Sopenharmony_ci goto sysfs_exit; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci return 0; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cisysfs_exit: 144262306a36Sopenharmony_ci zonefs_sysfs_exit(); 144362306a36Sopenharmony_cidestroy_inodecache: 144462306a36Sopenharmony_ci zonefs_destroy_inodecache(); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci return ret; 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic void __exit zonefs_exit(void) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci unregister_filesystem(&zonefs_type); 145262306a36Sopenharmony_ci zonefs_sysfs_exit(); 145362306a36Sopenharmony_ci zonefs_destroy_inodecache(); 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ciMODULE_AUTHOR("Damien Le Moal"); 145762306a36Sopenharmony_ciMODULE_DESCRIPTION("Zone file system for zoned block devices"); 145862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 145962306a36Sopenharmony_ciMODULE_ALIAS_FS("zonefs"); 146062306a36Sopenharmony_cimodule_init(zonefs_init); 146162306a36Sopenharmony_cimodule_exit(zonefs_exit); 1462