/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_FS_SUPER_H
#define _LINUX_FS_SUPER_H

#include <linux/fs/super_types.h>
#include <linux/unicode.h>


static inline void __sb_end_write(struct super_block *sb, int level)
{
	percpu_up_read(sb->s_writers.rw_sem + level - 1);
}

static inline void __sb_start_write(struct super_block *sb, int level)
{
	percpu_down_read_freezable(sb->s_writers.rw_sem + level - 1, true);
}

static inline bool __sb_start_write_trylock(struct super_block *sb, int level)
{
	return percpu_down_read_trylock(sb->s_writers.rw_sem + level - 1);
}

#define __sb_writers_acquired(sb, lev) \
	percpu_rwsem_acquire(&(sb)->s_writers.rw_sem[(lev) - 1], 1, _THIS_IP_)
#define __sb_writers_release(sb, lev) \
	percpu_rwsem_release(&(sb)->s_writers.rw_sem[(lev) - 1], _THIS_IP_)


static inline int __sb_write_started(const struct super_block *sb, int level)
{
	return lockdep_is_held_type(sb->s_writers.rw_sem + level - 1, 1);
}


static inline bool sb_write_started(const struct super_block *sb)
{
	return __sb_write_started(sb, SB_FREEZE_WRITE);
}


static inline bool sb_write_not_started(const struct super_block *sb)
{
	return __sb_write_started(sb, SB_FREEZE_WRITE) <= 0;
}


static inline void sb_end_write(struct super_block *sb)
{
	__sb_end_write(sb, SB_FREEZE_WRITE);
}


static inline void sb_end_pagefault(struct super_block *sb)
{
	__sb_end_write(sb, SB_FREEZE_PAGEFAULT);
}


static inline void sb_end_intwrite(struct super_block *sb)
{
	__sb_end_write(sb, SB_FREEZE_FS);
}


static inline void sb_start_write(struct super_block *sb)
{
	__sb_start_write(sb, SB_FREEZE_WRITE);
}

DEFINE_GUARD(super_write,
	     struct super_block *,
	     sb_start_write(_T),
	     sb_end_write(_T))

static inline bool sb_start_write_trylock(struct super_block *sb)
{
	return __sb_start_write_trylock(sb, SB_FREEZE_WRITE);
}


static inline void sb_start_pagefault(struct super_block *sb)
{
	__sb_start_write(sb, SB_FREEZE_PAGEFAULT);
}


static inline void sb_start_intwrite(struct super_block *sb)
{
	__sb_start_write(sb, SB_FREEZE_FS);
}

static inline bool sb_start_intwrite_trylock(struct super_block *sb)
{
	return __sb_start_write_trylock(sb, SB_FREEZE_FS);
}

static inline bool sb_rdonly(const struct super_block *sb)
{
	return sb->s_flags & SB_RDONLY;
}

static inline bool sb_is_blkdev_sb(struct super_block *sb)
{
	return IS_ENABLED(CONFIG_BLOCK) && sb == blockdev_superblock;
}

#if IS_ENABLED(CONFIG_UNICODE)
static inline struct unicode_map *sb_encoding(const struct super_block *sb)
{
	return sb->s_encoding;
}


static inline bool sb_same_encoding(const struct super_block *sb1,
				    const struct super_block *sb2)
{
	if (sb1->s_encoding == sb2->s_encoding)
		return true;

	return (sb1->s_encoding && sb2->s_encoding &&
		(sb1->s_encoding->version == sb2->s_encoding->version) &&
		(sb1->s_encoding_flags == sb2->s_encoding_flags));
}
#else
static inline struct unicode_map *sb_encoding(const struct super_block *sb)
{
	return NULL;
}

static inline bool sb_same_encoding(const struct super_block *sb1,
				    const struct super_block *sb2)
{
	return true;
}
#endif

static inline bool sb_has_encoding(const struct super_block *sb)
{
	return !!sb_encoding(sb);
}

int sb_set_blocksize(struct super_block *sb, int size);
int __must_check sb_min_blocksize(struct super_block *sb, int size);

int freeze_super(struct super_block *super, enum freeze_holder who,
		 const void *freeze_owner);
int thaw_super(struct super_block *super, enum freeze_holder who,
	       const void *freeze_owner);

#endif 
