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