| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- // SPDX-License-Identifier: GPL-2.0
- #include <linux/crc32.h>
- #include "messages.h"
- #include "fs.h"
- #include "accessors.h"
- #include "volumes.h"
- static const struct btrfs_csums {
- u16 size;
- const char name[10];
- } 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" },
- };
- /* 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;
- }
- size_t __attribute_const__ btrfs_get_num_csums(void)
- {
- return ARRAY_SIZE(btrfs_csums);
- }
- void btrfs_csum(u16 csum_type, const u8 *data, size_t len, u8 *out)
- {
- switch (csum_type) {
- case BTRFS_CSUM_TYPE_CRC32:
- put_unaligned_le32(~crc32c(~0, data, len), out);
- break;
- case BTRFS_CSUM_TYPE_XXHASH:
- put_unaligned_le64(xxh64(data, len, 0), out);
- break;
- case BTRFS_CSUM_TYPE_SHA256:
- sha256(data, len, out);
- break;
- case BTRFS_CSUM_TYPE_BLAKE2:
- blake2b(NULL, 0, data, len, out, 32);
- break;
- default:
- /* Checksum type is validated at mount time. */
- BUG();
- }
- }
- void btrfs_csum_init(struct btrfs_csum_ctx *ctx, u16 csum_type)
- {
- ctx->csum_type = csum_type;
- switch (ctx->csum_type) {
- case BTRFS_CSUM_TYPE_CRC32:
- ctx->crc32 = ~0;
- break;
- case BTRFS_CSUM_TYPE_XXHASH:
- xxh64_reset(&ctx->xxh64, 0);
- break;
- case BTRFS_CSUM_TYPE_SHA256:
- sha256_init(&ctx->sha256);
- break;
- case BTRFS_CSUM_TYPE_BLAKE2:
- blake2b_init(&ctx->blake2b, 32);
- break;
- default:
- /* Checksume type is validated at mount time. */
- BUG();
- }
- }
- void btrfs_csum_update(struct btrfs_csum_ctx *ctx, const u8 *data, size_t len)
- {
- switch (ctx->csum_type) {
- case BTRFS_CSUM_TYPE_CRC32:
- ctx->crc32 = crc32c(ctx->crc32, data, len);
- break;
- case BTRFS_CSUM_TYPE_XXHASH:
- xxh64_update(&ctx->xxh64, data, len);
- break;
- case BTRFS_CSUM_TYPE_SHA256:
- sha256_update(&ctx->sha256, data, len);
- break;
- case BTRFS_CSUM_TYPE_BLAKE2:
- blake2b_update(&ctx->blake2b, data, len);
- break;
- default:
- /* Checksum type is validated at mount time. */
- BUG();
- }
- }
- void btrfs_csum_final(struct btrfs_csum_ctx *ctx, u8 *out)
- {
- switch (ctx->csum_type) {
- case BTRFS_CSUM_TYPE_CRC32:
- put_unaligned_le32(~ctx->crc32, out);
- break;
- case BTRFS_CSUM_TYPE_XXHASH:
- put_unaligned_le64(xxh64_digest(&ctx->xxh64), out);
- break;
- case BTRFS_CSUM_TYPE_SHA256:
- sha256_final(&ctx->sha256, out);
- break;
- case BTRFS_CSUM_TYPE_BLAKE2:
- blake2b_final(&ctx->blake2b, out);
- break;
- default:
- /* Checksum type is validated at mount time. */
- BUG();
- }
- }
- /*
- * 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);
- }
- }
|