Contributors: 20
Author Tokens Token Proportion Commits Commit Proportion
Johannes Thumshirn 136 12.98% 5 9.80%
Miao Xie 113 10.78% 2 3.92%
David Sterba 110 10.50% 12 23.53%
Mitch Harder 103 9.83% 1 1.96%
Filipe David Borba Manana 87 8.30% 2 3.92%
Qu Wenruo 81 7.73% 4 7.84%
Josef Whiter 80 7.63% 6 11.76%
Liu Bo 65 6.20% 2 3.92%
Nikolay Borisov 54 5.15% 1 1.96%
Anand Jain 50 4.77% 1 1.96%
Jeff Mahoney 48 4.58% 4 7.84%
Omar Sandoval 36 3.44% 1 1.96%
Stefan Behrens 19 1.81% 2 3.92%
Goldwyn Rodrigues 18 1.72% 2 3.92%
Chris Mason 18 1.72% 1 1.96%
Frank Holton 12 1.15% 1 1.96%
xiaoshoukui 7 0.67% 1 1.96%
Naohiro Aota 6 0.57% 1 1.96%
Josef Bacik 3 0.29% 1 1.96%
Miklos Szeredi 2 0.19% 1 1.96%
Total 1048 51


// SPDX-License-Identifier: GPL-2.0

#include "messages.h"
#include "fs.h"
#include "accessors.h"
#include "volumes.h"

static const struct btrfs_csums {
	u16		size;
	const char	name[10];
	const char	driver[12];
} btrfs_csums[] = {
	[BTRFS_CSUM_TYPE_CRC32] = { .size = 4, .name = "crc32c" },
	[BTRFS_CSUM_TYPE_XXHASH] = { .size = 8, .name = "xxhash64" },
	[BTRFS_CSUM_TYPE_SHA256] = { .size = 32, .name = "sha256" },
	[BTRFS_CSUM_TYPE_BLAKE2] = { .size = 32, .name = "blake2b",
				     .driver = "blake2b-256" },
};

/* This exists for btrfs-progs usages. */
u16 btrfs_csum_type_size(u16 type)
{
	return btrfs_csums[type].size;
}

int btrfs_super_csum_size(const struct btrfs_super_block *s)
{
	u16 t = btrfs_super_csum_type(s);

	/* csum type is validated at mount time. */
	return btrfs_csum_type_size(t);
}

const char *btrfs_super_csum_name(u16 csum_type)
{
	/* csum type is validated at mount time. */
	return btrfs_csums[csum_type].name;
}

/*
 * Return driver name if defined, otherwise the name that's also a valid driver
 * name.
 */
const char *btrfs_super_csum_driver(u16 csum_type)
{
	/* csum type is validated at mount time */
	return btrfs_csums[csum_type].driver[0] ?
		btrfs_csums[csum_type].driver :
		btrfs_csums[csum_type].name;
}

size_t __attribute_const__ btrfs_get_num_csums(void)
{
	return ARRAY_SIZE(btrfs_csums);
}

/*
 * We support the following block sizes for all systems:
 *
 * - 4K
 *   This is the most common block size. For PAGE SIZE > 4K cases the subpage
 *   mode is used.
 *
 * - PAGE_SIZE
 *   The straightforward block size to support.
 *
 * And extra support for the following block sizes based on the kernel config:
 *
 * - MIN_BLOCKSIZE
 *   This is either 4K (regular builds) or 2K (debug builds)
 *   This allows testing subpage routines on x86_64.
 */
bool __attribute_const__ btrfs_supported_blocksize(u32 blocksize)
{
	/* @blocksize should be validated first. */
	ASSERT(is_power_of_2(blocksize) && blocksize >= BTRFS_MIN_BLOCKSIZE &&
	       blocksize <= BTRFS_MAX_BLOCKSIZE);

	if (blocksize == PAGE_SIZE || blocksize == SZ_4K || blocksize == BTRFS_MIN_BLOCKSIZE)
		return true;
#ifdef CONFIG_BTRFS_EXPERIMENTAL
	/*
	 * For bs > ps support it's done by specifying a minimal folio order
	 * for filemap, thus implying large data folios.
	 * For HIGHMEM systems, we can not always access the content of a (large)
	 * folio in one go, but go through them page by page.
	 *
	 * A lot of features don't implement a proper PAGE sized loop for large
	 * folios, this includes:
	 *
	 * - compression
	 * - verity
	 * - encoded write
	 *
	 * Considering HIGHMEM is such a pain to deal with and it's going
	 * to be deprecated eventually, just reject HIGHMEM && bs > ps cases.
	 */
	if (IS_ENABLED(CONFIG_HIGHMEM) && blocksize > PAGE_SIZE)
		return false;
	return true;
#endif
	return false;
}

/*
 * Start exclusive operation @type, return true on success.
 */
bool btrfs_exclop_start(struct btrfs_fs_info *fs_info,
			enum btrfs_exclusive_operation type)
{
	bool ret = false;

	spin_lock(&fs_info->super_lock);
	if (fs_info->exclusive_operation == BTRFS_EXCLOP_NONE) {
		fs_info->exclusive_operation = type;
		ret = true;
	}
	spin_unlock(&fs_info->super_lock);

	return ret;
}

/*
 * Conditionally allow to enter the exclusive operation in case it's compatible
 * with the running one.  This must be paired with btrfs_exclop_start_unlock()
 * and btrfs_exclop_finish().
 *
 * Compatibility:
 * - the same type is already running
 * - when trying to add a device and balance has been paused
 * - not BTRFS_EXCLOP_NONE - this is intentionally incompatible and the caller
 *   must check the condition first that would allow none -> @type
 */
bool btrfs_exclop_start_try_lock(struct btrfs_fs_info *fs_info,
				 enum btrfs_exclusive_operation type)
{
	spin_lock(&fs_info->super_lock);
	if (fs_info->exclusive_operation == type ||
	    (fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED &&
	     type == BTRFS_EXCLOP_DEV_ADD))
		return true;

	spin_unlock(&fs_info->super_lock);
	return false;
}

void btrfs_exclop_start_unlock(struct btrfs_fs_info *fs_info)
{
	spin_unlock(&fs_info->super_lock);
}

void btrfs_exclop_finish(struct btrfs_fs_info *fs_info)
{
	spin_lock(&fs_info->super_lock);
	WRITE_ONCE(fs_info->exclusive_operation, BTRFS_EXCLOP_NONE);
	spin_unlock(&fs_info->super_lock);
	sysfs_notify(&fs_info->fs_devices->fsid_kobj, NULL, "exclusive_operation");
}

void btrfs_exclop_balance(struct btrfs_fs_info *fs_info,
			  enum btrfs_exclusive_operation op)
{
	switch (op) {
	case BTRFS_EXCLOP_BALANCE_PAUSED:
		spin_lock(&fs_info->super_lock);
		ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE ||
		       fs_info->exclusive_operation == BTRFS_EXCLOP_DEV_ADD ||
		       fs_info->exclusive_operation == BTRFS_EXCLOP_NONE ||
		       fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED);
		fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE_PAUSED;
		spin_unlock(&fs_info->super_lock);
		break;
	case BTRFS_EXCLOP_BALANCE:
		spin_lock(&fs_info->super_lock);
		ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED);
		fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE;
		spin_unlock(&fs_info->super_lock);
		break;
	default:
		btrfs_warn(fs_info,
			"invalid exclop balance operation %d requested", op);
	}
}

void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag,
			     const char *name)
{
	struct btrfs_super_block *disk_super;
	u64 features;

	disk_super = fs_info->super_copy;
	features = btrfs_super_incompat_flags(disk_super);
	if (!(features & flag)) {
		spin_lock(&fs_info->super_lock);
		features = btrfs_super_incompat_flags(disk_super);
		if (!(features & flag)) {
			features |= flag;
			btrfs_set_super_incompat_flags(disk_super, features);
			btrfs_info(fs_info,
				"setting incompat feature flag for %s (0x%llx)",
				name, flag);
		}
		spin_unlock(&fs_info->super_lock);
		set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
	}
}

void __btrfs_clear_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag,
			       const char *name)
{
	struct btrfs_super_block *disk_super;
	u64 features;

	disk_super = fs_info->super_copy;
	features = btrfs_super_incompat_flags(disk_super);
	if (features & flag) {
		spin_lock(&fs_info->super_lock);
		features = btrfs_super_incompat_flags(disk_super);
		if (features & flag) {
			features &= ~flag;
			btrfs_set_super_incompat_flags(disk_super, features);
			btrfs_info(fs_info,
				"clearing incompat feature flag for %s (0x%llx)",
				name, flag);
		}
		spin_unlock(&fs_info->super_lock);
		set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
	}
}

void __btrfs_set_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
			      const char *name)
{
	struct btrfs_super_block *disk_super;
	u64 features;

	disk_super = fs_info->super_copy;
	features = btrfs_super_compat_ro_flags(disk_super);
	if (!(features & flag)) {
		spin_lock(&fs_info->super_lock);
		features = btrfs_super_compat_ro_flags(disk_super);
		if (!(features & flag)) {
			features |= flag;
			btrfs_set_super_compat_ro_flags(disk_super, features);
			btrfs_info(fs_info,
				"setting compat-ro feature flag for %s (0x%llx)",
				name, flag);
		}
		spin_unlock(&fs_info->super_lock);
		set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
	}
}

void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
				const char *name)
{
	struct btrfs_super_block *disk_super;
	u64 features;

	disk_super = fs_info->super_copy;
	features = btrfs_super_compat_ro_flags(disk_super);
	if (features & flag) {
		spin_lock(&fs_info->super_lock);
		features = btrfs_super_compat_ro_flags(disk_super);
		if (features & flag) {
			features &= ~flag;
			btrfs_set_super_compat_ro_flags(disk_super, features);
			btrfs_info(fs_info,
				"clearing compat-ro feature flag for %s (0x%llx)",
				name, flag);
		}
		spin_unlock(&fs_info->super_lock);
		set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
	}
}