Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-5.15.y' into android14-5.15
* aosp/upstream-f2fs-stable-linux-5.15.y: f2fs: use onstack pages instead of pvec f2fs: intorduce f2fs_all_cluster_page_ready f2fs: clean up f2fs_abort_atomic_write() f2fs: handle decompress only post processing in softirq f2fs: do not allow to decompress files have FI_COMPRESS_RELEASED f2fs: do not set compression bit if kernel doesn't support f2fs: remove device type check for direct IO f2fs: fix null-ptr-deref in f2fs_get_dnode_of_data f2fs: revive F2FS_IOC_ABORT_VOLATILE_WRITE lib/iov_iter: initialize "flags" in new pipe_buffer f2fs: fix to do sanity check on segment type in build_sit_entries() f2fs: obsolete unused MAX_DISCARD_BLOCKS f2fs: fix to avoid use f2fs_bug_on() in f2fs_new_node_page() f2fs: fix to remove F2FS_COMPR_FL and tag F2FS_NOCOMP_FL at the same time f2fs: introduce sysfs atomic write statistics f2fs: don't bother wait_ms by foreground gc f2fs: invalidate meta pages only for post_read required inode f2fs: allow compression of files without blocks f2fs: fix to check inline_data during compressed inode conversion f2fs: Delete f2fs_copy_page() and replace with memcpy_page() f2fs: fix to invalidate META_MAPPING before DIO write f2fs: add a sysfs entry to show zone capacity f2fs: adjust zone capacity when considering valid block count f2fs: enforce single zone capacity f2fs: remove redundant code for gc condition f2fs: introduce memory mode f2fs: initialize page_array_entry slab only if compression feature is on f2fs: optimize error handling in redirty_blocks f2fs: do not skip updating inode when retrying to flush node page f2fs: use the updated test_dummy_encryption helper functions f2fs: do not count ENOENT for error case f2fs: fix iostat related lock protection f2fs: attach inline_data after setting compression BACKPORT: block: simplify calling convention of elv_unregister_queue() UPSTREAM: blk-crypto: remove blk_crypto_unregister() UPSTREAM: blk-crypto: update inline encryption documentation BACKPORT: blk-crypto: rename blk_keyslot_manager to blk_crypto_profile UPSTREAM: blk-crypto: rename keyslot-manager files to blk-crypto-profile UPSTREAM: blk-crypto-fallback: properly prefix function and struct names fscrypt: add new helper functions for test_dummy_encryption fscrypt: factor out fscrypt_policy_to_key_spec() fscrypt: log when starting to use inline encryption fscrypt: split up FS_CRYPTO_BLOCK_SIZE Bug: 228919347 Signed-off-by: Jaegeuk Kim <jaegeuk@google.com> Change-Id: I45cd7f2e740d19fa477481906b5b42cfd82c6bc8
This commit is contained in:
@@ -580,3 +580,33 @@ Date: January 2022
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description: Controls max # of node block writes to be used for roll forward
|
||||
recovery. This can limit the roll forward recovery time.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/unusable_blocks_per_sec
|
||||
Date: June 2022
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description: Shows the number of unusable blocks in a section which was defined by
|
||||
the zone capacity reported by underlying zoned device.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/current_atomic_write
|
||||
Date: July 2022
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: Show the total current atomic write block count, which is not committed yet.
|
||||
This is a read-only entry.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/peak_atomic_write
|
||||
Date: July 2022
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: Show the peak value of total current atomic write block count after boot.
|
||||
If you write "0" here, you can initialize to "0".
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/committed_atomic_block
|
||||
Date: July 2022
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: Show the accumulated total committed atomic write block count after boot.
|
||||
If you write "0" here, you can initialize to "0".
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/revoked_atomic_block
|
||||
Date: July 2022
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: Show the accumulated total revoked atomic write block count after boot.
|
||||
If you write "0" here, you can initialize to "0".
|
||||
|
||||
@@ -336,6 +336,11 @@ discard_unit=%s Control discard unit, the argument can be "block", "segment"
|
||||
default, it is helpful for large sized SMR or ZNS devices to
|
||||
reduce memory cost by getting rid of fs metadata supports small
|
||||
discard.
|
||||
memory=%s Control memory mode. This supports "normal" and "low" modes.
|
||||
"low" mode is introduced to support low memory devices.
|
||||
Because of the nature of low memory devices, in this mode, f2fs
|
||||
will try to save memory sometimes by sacrificing performance.
|
||||
"normal" mode is the default mode and same as before.
|
||||
======================== ============================================================
|
||||
|
||||
Debugfs Entries
|
||||
|
||||
@@ -113,7 +113,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
|
||||
|
||||
if (WARN_ON_ONCE(len <= 0))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(len % FS_CRYPTO_BLOCK_SIZE != 0))
|
||||
if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0))
|
||||
return -EINVAL;
|
||||
|
||||
fscrypt_generate_iv(&iv, lblk_num, ci);
|
||||
@@ -213,8 +213,8 @@ EXPORT_SYMBOL(fscrypt_encrypt_pagecache_blocks);
|
||||
* fscrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place
|
||||
* @inode: The inode to which this block belongs
|
||||
* @page: The page containing the block to encrypt
|
||||
* @len: Size of block to encrypt. Doesn't need to be a multiple of the
|
||||
* fs block size, but must be a multiple of FS_CRYPTO_BLOCK_SIZE.
|
||||
* @len: Size of block to encrypt. This must be a multiple of
|
||||
* FSCRYPT_CONTENTS_ALIGNMENT.
|
||||
* @offs: Byte offset within @page at which the block to encrypt begins
|
||||
* @lblk_num: Filesystem logical block number of the block, i.e. the 0-based
|
||||
* number of the block within the file
|
||||
@@ -283,8 +283,8 @@ EXPORT_SYMBOL(fscrypt_decrypt_pagecache_blocks);
|
||||
* fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place
|
||||
* @inode: The inode to which this block belongs
|
||||
* @page: The page containing the block to decrypt
|
||||
* @len: Size of block to decrypt. Doesn't need to be a multiple of the
|
||||
* fs block size, but must be a multiple of FS_CRYPTO_BLOCK_SIZE.
|
||||
* @len: Size of block to decrypt. This must be a multiple of
|
||||
* FSCRYPT_CONTENTS_ALIGNMENT.
|
||||
* @offs: Byte offset within @page at which the block to decrypt begins
|
||||
* @lblk_num: Filesystem logical block number of the block, i.e. the 0-based
|
||||
* number of the block within the file
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
#include <crypto/skcipher.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/*
|
||||
* The minimum message length (input and output length), in bytes, for all
|
||||
* filenames encryption modes. Filenames shorter than this will be zero-padded
|
||||
* before being encrypted.
|
||||
*/
|
||||
#define FSCRYPT_FNAME_MIN_MSG_LEN 16
|
||||
|
||||
/*
|
||||
* struct fscrypt_nokey_name - identifier for directory entry when key is absent
|
||||
*
|
||||
@@ -267,7 +274,7 @@ bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
|
||||
|
||||
if (orig_len > max_len)
|
||||
return false;
|
||||
encrypted_len = max(orig_len, (u32)FS_CRYPTO_BLOCK_SIZE);
|
||||
encrypted_len = max_t(u32, orig_len, FSCRYPT_FNAME_MIN_MSG_LEN);
|
||||
encrypted_len = round_up(encrypted_len, padding);
|
||||
*encrypted_len_ret = min(encrypted_len, max_len);
|
||||
return true;
|
||||
@@ -350,7 +357,7 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iname->len < FS_CRYPTO_BLOCK_SIZE)
|
||||
if (iname->len < FSCRYPT_FNAME_MIN_MSG_LEN)
|
||||
return -EUCLEAN;
|
||||
|
||||
if (fscrypt_has_encryption_key(inode))
|
||||
|
||||
@@ -602,8 +602,8 @@ struct key *
|
||||
fscrypt_find_master_key(struct super_block *sb,
|
||||
const struct fscrypt_key_specifier *mk_spec);
|
||||
|
||||
int fscrypt_add_test_dummy_key(struct super_block *sb,
|
||||
struct fscrypt_key_specifier *key_spec);
|
||||
int fscrypt_get_test_dummy_key_identifier(
|
||||
u8 key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
|
||||
|
||||
int fscrypt_verify_key_added(struct super_block *sb,
|
||||
const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
|
||||
@@ -618,7 +618,9 @@ struct fscrypt_mode {
|
||||
int keysize; /* key size in bytes */
|
||||
int security_strength; /* security strength in bytes */
|
||||
int ivsize; /* IV size in bytes */
|
||||
int logged_impl_name;
|
||||
int logged_cryptoapi_impl;
|
||||
int logged_blk_crypto_native;
|
||||
int logged_blk_crypto_fallback;
|
||||
enum blk_crypto_mode_num blk_crypto_mode;
|
||||
};
|
||||
|
||||
@@ -678,6 +680,8 @@ int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci);
|
||||
|
||||
bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
|
||||
const union fscrypt_policy *policy2);
|
||||
int fscrypt_policy_to_key_spec(const union fscrypt_policy *policy,
|
||||
struct fscrypt_key_specifier *key_spec);
|
||||
bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
|
||||
const struct inode *inode);
|
||||
int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* provides the key and IV to use.
|
||||
*/
|
||||
|
||||
#include <linux/blk-crypto.h>
|
||||
#include <linux/blk-crypto-profile.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/buffer_head.h>
|
||||
@@ -65,6 +64,35 @@ static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci)
|
||||
return DIV_ROUND_UP(lblk_bits, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Log a message when starting to use blk-crypto (native) or blk-crypto-fallback
|
||||
* for an encryption mode for the first time. This is the blk-crypto
|
||||
* counterpart to the message logged when starting to use the crypto API for the
|
||||
* first time. A limitation is that these messages don't convey which specific
|
||||
* filesystems or files are using each implementation. However, *usually*
|
||||
* systems use just one implementation per mode, which makes these messages
|
||||
* helpful for debugging problems where the "wrong" implementation is used.
|
||||
*/
|
||||
static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode,
|
||||
struct request_queue **devs,
|
||||
int num_devs,
|
||||
const struct blk_crypto_config *cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_devs; i++) {
|
||||
if (!IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) ||
|
||||
__blk_crypto_cfg_supported(devs[i]->crypto_profile, cfg)) {
|
||||
if (!xchg(&mode->logged_blk_crypto_native, 1))
|
||||
pr_info("fscrypt: %s using blk-crypto (native)\n",
|
||||
mode->friendly_name);
|
||||
} else if (!xchg(&mode->logged_blk_crypto_fallback, 1)) {
|
||||
pr_info("fscrypt: %s using blk-crypto-fallback\n",
|
||||
mode->friendly_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable inline encryption for this file if supported. */
|
||||
int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
|
||||
bool is_hw_wrapped_key)
|
||||
@@ -122,6 +150,8 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
|
||||
goto out_free_devs;
|
||||
}
|
||||
|
||||
fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg);
|
||||
|
||||
ci->ci_inlinecrypt = true;
|
||||
out_free_devs:
|
||||
kfree(devs);
|
||||
|
||||
@@ -719,29 +719,69 @@ out_wipe_secret:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
|
||||
|
||||
/*
|
||||
* Add the key for '-o test_dummy_encryption' to the filesystem keyring.
|
||||
*
|
||||
* Use a per-boot random key to prevent people from misusing this option.
|
||||
*/
|
||||
int fscrypt_add_test_dummy_key(struct super_block *sb,
|
||||
struct fscrypt_key_specifier *key_spec)
|
||||
static void
|
||||
fscrypt_get_test_dummy_secret(struct fscrypt_master_key_secret *secret)
|
||||
{
|
||||
static u8 test_key[FSCRYPT_MAX_STANDARD_KEY_SIZE];
|
||||
struct fscrypt_master_key_secret secret;
|
||||
int err;
|
||||
|
||||
get_random_once(test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE);
|
||||
|
||||
memset(&secret, 0, sizeof(secret));
|
||||
secret.size = FSCRYPT_MAX_STANDARD_KEY_SIZE;
|
||||
memcpy(secret.raw, test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE);
|
||||
memset(secret, 0, sizeof(*secret));
|
||||
secret->size = FSCRYPT_MAX_STANDARD_KEY_SIZE;
|
||||
memcpy(secret->raw, test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE);
|
||||
}
|
||||
|
||||
err = add_master_key(sb, &secret, key_spec);
|
||||
int fscrypt_get_test_dummy_key_identifier(
|
||||
u8 key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE])
|
||||
{
|
||||
struct fscrypt_master_key_secret secret;
|
||||
int err;
|
||||
|
||||
fscrypt_get_test_dummy_secret(&secret);
|
||||
|
||||
err = fscrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size);
|
||||
if (err)
|
||||
goto out;
|
||||
err = fscrypt_hkdf_expand(&secret.hkdf, HKDF_CONTEXT_KEY_IDENTIFIER,
|
||||
NULL, 0, key_identifier,
|
||||
FSCRYPT_KEY_IDENTIFIER_SIZE);
|
||||
out:
|
||||
wipe_master_key_secret(&secret);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscrypt_add_test_dummy_key() - add the test dummy encryption key
|
||||
* @sb: the filesystem instance to add the key to
|
||||
* @dummy_policy: the encryption policy for test_dummy_encryption
|
||||
*
|
||||
* If needed, add the key for the test_dummy_encryption mount option to the
|
||||
* filesystem. To prevent misuse of this mount option, a per-boot random key is
|
||||
* used instead of a hardcoded one. This makes it so that any encrypted files
|
||||
* created using this option won't be accessible after a reboot.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_add_test_dummy_key(struct super_block *sb,
|
||||
const struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
const union fscrypt_policy *policy = dummy_policy->policy;
|
||||
struct fscrypt_key_specifier key_spec;
|
||||
struct fscrypt_master_key_secret secret;
|
||||
int err;
|
||||
|
||||
if (!policy)
|
||||
return 0;
|
||||
err = fscrypt_policy_to_key_spec(policy, &key_spec);
|
||||
if (err)
|
||||
return err;
|
||||
fscrypt_get_test_dummy_secret(&secret);
|
||||
err = add_master_key(sb, &secret, &key_spec);
|
||||
wipe_master_key_secret(&secret);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_add_test_dummy_key);
|
||||
|
||||
/*
|
||||
* Verify that the current user has added a master key with the given identifier
|
||||
* (returns -ENOKEY if not). This is needed to prevent a user from encrypting
|
||||
|
||||
@@ -94,7 +94,7 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
|
||||
mode->cipher_str, PTR_ERR(tfm));
|
||||
return tfm;
|
||||
}
|
||||
if (!xchg(&mode->logged_impl_name, 1)) {
|
||||
if (!xchg(&mode->logged_cryptoapi_impl, 1)) {
|
||||
/*
|
||||
* fscrypt performance can vary greatly depending on which
|
||||
* crypto algorithm implementation is used. Help people debug
|
||||
@@ -467,23 +467,9 @@ static int setup_file_encryption_key(struct fscrypt_info *ci,
|
||||
struct fscrypt_key_specifier mk_spec;
|
||||
int err;
|
||||
|
||||
switch (ci->ci_policy.version) {
|
||||
case FSCRYPT_POLICY_V1:
|
||||
mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
|
||||
memcpy(mk_spec.u.descriptor,
|
||||
ci->ci_policy.v1.master_key_descriptor,
|
||||
FSCRYPT_KEY_DESCRIPTOR_SIZE);
|
||||
break;
|
||||
case FSCRYPT_POLICY_V2:
|
||||
mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
|
||||
memcpy(mk_spec.u.identifier,
|
||||
ci->ci_policy.v2.master_key_identifier,
|
||||
FSCRYPT_KEY_IDENTIFIER_SIZE);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
key = fscrypt_find_master_key(ci->ci_inode->i_sb, &mk_spec);
|
||||
if (IS_ERR(key)) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* Modified by Eric Biggers, 2019 for v2 policy support.
|
||||
*/
|
||||
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string.h>
|
||||
@@ -32,6 +33,26 @@ bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
|
||||
return !memcmp(policy1, policy2, fscrypt_policy_size(policy1));
|
||||
}
|
||||
|
||||
int fscrypt_policy_to_key_spec(const union fscrypt_policy *policy,
|
||||
struct fscrypt_key_specifier *key_spec)
|
||||
{
|
||||
switch (policy->version) {
|
||||
case FSCRYPT_POLICY_V1:
|
||||
key_spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
|
||||
memcpy(key_spec->u.descriptor, policy->v1.master_key_descriptor,
|
||||
FSCRYPT_KEY_DESCRIPTOR_SIZE);
|
||||
return 0;
|
||||
case FSCRYPT_POLICY_V2:
|
||||
key_spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
|
||||
memcpy(key_spec->u.identifier, policy->v2.master_key_identifier,
|
||||
FSCRYPT_KEY_IDENTIFIER_SIZE);
|
||||
return 0;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const union fscrypt_policy *
|
||||
fscrypt_get_dummy_policy(struct super_block *sb)
|
||||
{
|
||||
@@ -704,73 +725,45 @@ int fscrypt_set_context(struct inode *inode, void *fs_data)
|
||||
EXPORT_SYMBOL_GPL(fscrypt_set_context);
|
||||
|
||||
/**
|
||||
* fscrypt_set_test_dummy_encryption() - handle '-o test_dummy_encryption'
|
||||
* @sb: the filesystem on which test_dummy_encryption is being specified
|
||||
* @arg: the argument to the test_dummy_encryption option. May be NULL.
|
||||
* @dummy_policy: the filesystem's current dummy policy (input/output, see
|
||||
* below)
|
||||
* fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option
|
||||
* @param: the mount option
|
||||
* @dummy_policy: (input/output) the place to write the dummy policy that will
|
||||
* result from parsing the option. Zero-initialize this. If a policy is
|
||||
* already set here (due to test_dummy_encryption being given multiple
|
||||
* times), then this function will verify that the policies are the same.
|
||||
*
|
||||
* Handle the test_dummy_encryption mount option by creating a dummy encryption
|
||||
* policy, saving it in @dummy_policy, and adding the corresponding dummy
|
||||
* encryption key to the filesystem. If the @dummy_policy is already set, then
|
||||
* instead validate that it matches @arg. Don't support changing it via
|
||||
* remount, as that is difficult to do safely.
|
||||
*
|
||||
* Return: 0 on success (dummy policy set, or the same policy is already set);
|
||||
* -EEXIST if a different dummy policy is already set;
|
||||
* or another -errno value.
|
||||
* Return: 0 on success; -EINVAL if the argument is invalid; -EEXIST if the
|
||||
* argument conflicts with one already specified; or -ENOMEM.
|
||||
*/
|
||||
int fscrypt_set_test_dummy_encryption(struct super_block *sb, const char *arg,
|
||||
struct fscrypt_dummy_policy *dummy_policy)
|
||||
int fscrypt_parse_test_dummy_encryption(const struct fs_parameter *param,
|
||||
struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
struct fscrypt_key_specifier key_spec = { 0 };
|
||||
int version;
|
||||
union fscrypt_policy *policy = NULL;
|
||||
const char *arg = "v2";
|
||||
union fscrypt_policy *policy;
|
||||
int err;
|
||||
|
||||
if (!arg)
|
||||
arg = "v2";
|
||||
|
||||
if (!strcmp(arg, "v1")) {
|
||||
version = FSCRYPT_POLICY_V1;
|
||||
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
|
||||
memset(key_spec.u.descriptor, 0x42,
|
||||
FSCRYPT_KEY_DESCRIPTOR_SIZE);
|
||||
} else if (!strcmp(arg, "v2")) {
|
||||
version = FSCRYPT_POLICY_V2;
|
||||
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
|
||||
/* key_spec.u.identifier gets filled in when adding the key */
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (param->type == fs_value_is_string && *param->string)
|
||||
arg = param->string;
|
||||
|
||||
policy = kzalloc(sizeof(*policy), GFP_KERNEL);
|
||||
if (!policy) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!policy)
|
||||
return -ENOMEM;
|
||||
|
||||
err = fscrypt_add_test_dummy_key(sb, &key_spec);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
policy->version = version;
|
||||
switch (policy->version) {
|
||||
case FSCRYPT_POLICY_V1:
|
||||
if (!strcmp(arg, "v1")) {
|
||||
policy->version = FSCRYPT_POLICY_V1;
|
||||
policy->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
|
||||
policy->v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
|
||||
memcpy(policy->v1.master_key_descriptor, key_spec.u.descriptor,
|
||||
memset(policy->v1.master_key_descriptor, 0x42,
|
||||
FSCRYPT_KEY_DESCRIPTOR_SIZE);
|
||||
break;
|
||||
case FSCRYPT_POLICY_V2:
|
||||
} else if (!strcmp(arg, "v2")) {
|
||||
policy->version = FSCRYPT_POLICY_V2;
|
||||
policy->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
|
||||
policy->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
|
||||
memcpy(policy->v2.master_key_identifier, key_spec.u.identifier,
|
||||
FSCRYPT_KEY_IDENTIFIER_SIZE);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
err = fscrypt_get_test_dummy_key_identifier(
|
||||
policy->v2.master_key_identifier);
|
||||
if (err)
|
||||
goto out;
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@@ -789,6 +782,37 @@ out:
|
||||
kfree(policy);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_parse_test_dummy_encryption);
|
||||
|
||||
/**
|
||||
* fscrypt_dummy_policies_equal() - check whether two dummy policies are equal
|
||||
* @p1: the first test dummy policy (may be unset)
|
||||
* @p2: the second test dummy policy (may be unset)
|
||||
*
|
||||
* Return: %true if the dummy policies are both set and equal, or both unset.
|
||||
*/
|
||||
bool fscrypt_dummy_policies_equal(const struct fscrypt_dummy_policy *p1,
|
||||
const struct fscrypt_dummy_policy *p2)
|
||||
{
|
||||
if (!p1->policy && !p2->policy)
|
||||
return true;
|
||||
if (!p1->policy || !p2->policy)
|
||||
return false;
|
||||
return fscrypt_policies_equal(p1->policy, p2->policy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_dummy_policies_equal);
|
||||
|
||||
/* Deprecated, do not use */
|
||||
int fscrypt_set_test_dummy_encryption(struct super_block *sb, const char *arg,
|
||||
struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
struct fs_parameter param = {
|
||||
.type = fs_value_is_string,
|
||||
.string = arg ? (char *)arg : "",
|
||||
};
|
||||
return fscrypt_parse_test_dummy_encryption(¶m, dummy_policy) ?:
|
||||
fscrypt_add_test_dummy_key(sb, dummy_policy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption);
|
||||
|
||||
/**
|
||||
|
||||
@@ -728,14 +728,19 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
|
||||
static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
|
||||
bool pre_alloc);
|
||||
static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
|
||||
bool bypass_destroy_callback, bool pre_alloc);
|
||||
|
||||
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(dic->inode);
|
||||
const struct f2fs_compress_ops *cops =
|
||||
f2fs_cops[fi->i_compress_algorithm];
|
||||
bool bypass_callback = false;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
trace_f2fs_decompress_pages_start(dic->inode, dic->cluster_idx,
|
||||
dic->cluster_size, fi->i_compress_algorithm);
|
||||
@@ -745,41 +750,10 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
|
||||
goto out_end_io;
|
||||
}
|
||||
|
||||
dic->tpages = page_array_alloc(dic->inode, dic->cluster_size);
|
||||
if (!dic->tpages) {
|
||||
ret = -ENOMEM;
|
||||
goto out_end_io;
|
||||
}
|
||||
|
||||
for (i = 0; i < dic->cluster_size; i++) {
|
||||
if (dic->rpages[i]) {
|
||||
dic->tpages[i] = dic->rpages[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
dic->tpages[i] = f2fs_compress_alloc_page();
|
||||
if (!dic->tpages[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_end_io;
|
||||
}
|
||||
}
|
||||
|
||||
if (cops->init_decompress_ctx) {
|
||||
ret = cops->init_decompress_ctx(dic);
|
||||
if (ret)
|
||||
goto out_end_io;
|
||||
}
|
||||
|
||||
dic->rbuf = f2fs_vmap(dic->tpages, dic->cluster_size);
|
||||
if (!dic->rbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto out_destroy_decompress_ctx;
|
||||
}
|
||||
|
||||
dic->cbuf = f2fs_vmap(dic->cpages, dic->nr_cpages);
|
||||
if (!dic->cbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto out_vunmap_rbuf;
|
||||
ret = f2fs_prepare_decomp_mem(dic, false);
|
||||
if (ret) {
|
||||
bypass_callback = true;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
dic->clen = le32_to_cpu(dic->cbuf->clen);
|
||||
@@ -787,7 +761,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
|
||||
|
||||
if (dic->clen > PAGE_SIZE * dic->nr_cpages - COMPRESS_HEADER_SIZE) {
|
||||
ret = -EFSCORRUPTED;
|
||||
goto out_vunmap_cbuf;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
ret = cops->decompress_pages(dic);
|
||||
@@ -808,17 +782,13 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
|
||||
}
|
||||
}
|
||||
|
||||
out_vunmap_cbuf:
|
||||
vm_unmap_ram(dic->cbuf, dic->nr_cpages);
|
||||
out_vunmap_rbuf:
|
||||
vm_unmap_ram(dic->rbuf, dic->cluster_size);
|
||||
out_destroy_decompress_ctx:
|
||||
if (cops->destroy_decompress_ctx)
|
||||
cops->destroy_decompress_ctx(dic);
|
||||
out_release:
|
||||
f2fs_release_decomp_mem(dic, bypass_callback, false);
|
||||
|
||||
out_end_io:
|
||||
trace_f2fs_decompress_pages_end(dic->inode, dic->cluster_idx,
|
||||
dic->clen, ret);
|
||||
f2fs_decompress_end_io(dic, ret);
|
||||
f2fs_decompress_end_io(dic, ret, in_task);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -828,7 +798,7 @@ out_end_io:
|
||||
* (or in the case of a failure, cleans up without actually decompressing).
|
||||
*/
|
||||
void f2fs_end_read_compressed_page(struct page *page, bool failed,
|
||||
block_t blkaddr)
|
||||
block_t blkaddr, bool in_task)
|
||||
{
|
||||
struct decompress_io_ctx *dic =
|
||||
(struct decompress_io_ctx *)page_private(page);
|
||||
@@ -838,12 +808,12 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed,
|
||||
|
||||
if (failed)
|
||||
WRITE_ONCE(dic->failed, true);
|
||||
else if (blkaddr)
|
||||
else if (blkaddr && in_task)
|
||||
f2fs_cache_compressed_page(sbi, page,
|
||||
dic->inode->i_ino, blkaddr);
|
||||
|
||||
if (atomic_dec_and_test(&dic->remaining_pages))
|
||||
f2fs_decompress_cluster(dic);
|
||||
f2fs_decompress_cluster(dic, in_task);
|
||||
}
|
||||
|
||||
static bool is_page_in_cluster(struct compress_ctx *cc, pgoff_t index)
|
||||
@@ -870,19 +840,26 @@ bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index)
|
||||
return is_page_in_cluster(cc, index);
|
||||
}
|
||||
|
||||
bool f2fs_all_cluster_page_loaded(struct compress_ctx *cc, struct pagevec *pvec,
|
||||
int index, int nr_pages)
|
||||
bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
|
||||
int index, int nr_pages, bool uptodate)
|
||||
{
|
||||
unsigned long pgidx;
|
||||
int i;
|
||||
unsigned long pgidx = pages[index]->index;
|
||||
int i = uptodate ? 0 : 1;
|
||||
|
||||
/*
|
||||
* when uptodate set to true, try to check all pages in cluster is
|
||||
* uptodate or not.
|
||||
*/
|
||||
if (uptodate && (pgidx % cc->cluster_size))
|
||||
return false;
|
||||
|
||||
if (nr_pages - index < cc->cluster_size)
|
||||
return false;
|
||||
|
||||
pgidx = pvec->pages[index]->index;
|
||||
|
||||
for (i = 1; i < cc->cluster_size; i++) {
|
||||
if (pvec->pages[index + i]->index != pgidx + i)
|
||||
for (; i < cc->cluster_size; i++) {
|
||||
if (pages[index + i]->index != pgidx + i)
|
||||
return false;
|
||||
if (uptodate && !PageUptodate(pages[index + i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1551,16 +1528,85 @@ destroy_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void f2fs_free_dic(struct decompress_io_ctx *dic);
|
||||
static inline bool allow_memalloc_for_decomp(struct f2fs_sb_info *sbi,
|
||||
bool pre_alloc)
|
||||
{
|
||||
return pre_alloc ^ f2fs_low_mem_mode(sbi);
|
||||
}
|
||||
|
||||
static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
|
||||
bool pre_alloc)
|
||||
{
|
||||
const struct f2fs_compress_ops *cops =
|
||||
f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
|
||||
int i;
|
||||
|
||||
if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
|
||||
return 0;
|
||||
|
||||
dic->tpages = page_array_alloc(dic->inode, dic->cluster_size);
|
||||
if (!dic->tpages)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < dic->cluster_size; i++) {
|
||||
if (dic->rpages[i]) {
|
||||
dic->tpages[i] = dic->rpages[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
dic->tpages[i] = f2fs_compress_alloc_page();
|
||||
if (!dic->tpages[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dic->rbuf = f2fs_vmap(dic->tpages, dic->cluster_size);
|
||||
if (!dic->rbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
dic->cbuf = f2fs_vmap(dic->cpages, dic->nr_cpages);
|
||||
if (!dic->cbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cops->init_decompress_ctx) {
|
||||
int ret = cops->init_decompress_ctx(dic);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
|
||||
bool bypass_destroy_callback, bool pre_alloc)
|
||||
{
|
||||
const struct f2fs_compress_ops *cops =
|
||||
f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
|
||||
|
||||
if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
|
||||
return;
|
||||
|
||||
if (!bypass_destroy_callback && cops->destroy_decompress_ctx)
|
||||
cops->destroy_decompress_ctx(dic);
|
||||
|
||||
if (dic->cbuf)
|
||||
vm_unmap_ram(dic->cbuf, dic->nr_cpages);
|
||||
|
||||
if (dic->rbuf)
|
||||
vm_unmap_ram(dic->rbuf, dic->cluster_size);
|
||||
}
|
||||
|
||||
static void f2fs_free_dic(struct decompress_io_ctx *dic,
|
||||
bool bypass_destroy_callback);
|
||||
|
||||
struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
|
||||
{
|
||||
struct decompress_io_ctx *dic;
|
||||
pgoff_t start_idx = start_idx_of_cluster(cc);
|
||||
int i;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
|
||||
int i, ret;
|
||||
|
||||
dic = f2fs_kmem_cache_alloc(dic_entry_slab, GFP_F2FS_ZERO,
|
||||
false, F2FS_I_SB(cc->inode));
|
||||
dic = f2fs_kmem_cache_alloc(dic_entry_slab, GFP_F2FS_ZERO, false, sbi);
|
||||
if (!dic)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -1586,32 +1632,43 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
|
||||
dic->nr_rpages = cc->cluster_size;
|
||||
|
||||
dic->cpages = page_array_alloc(dic->inode, dic->nr_cpages);
|
||||
if (!dic->cpages)
|
||||
if (!dic->cpages) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < dic->nr_cpages; i++) {
|
||||
struct page *page;
|
||||
|
||||
page = f2fs_compress_alloc_page();
|
||||
if (!page)
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
f2fs_set_compressed_page(page, cc->inode,
|
||||
start_idx + i + 1, dic);
|
||||
dic->cpages[i] = page;
|
||||
}
|
||||
|
||||
ret = f2fs_prepare_decomp_mem(dic, true);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
return dic;
|
||||
|
||||
out_free:
|
||||
f2fs_free_dic(dic);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
f2fs_free_dic(dic, true);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void f2fs_free_dic(struct decompress_io_ctx *dic)
|
||||
static void f2fs_free_dic(struct decompress_io_ctx *dic,
|
||||
bool bypass_destroy_callback)
|
||||
{
|
||||
int i;
|
||||
|
||||
f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
|
||||
|
||||
if (dic->tpages) {
|
||||
for (i = 0; i < dic->cluster_size; i++) {
|
||||
if (dic->rpages[i])
|
||||
@@ -1636,17 +1693,33 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic)
|
||||
kmem_cache_free(dic_entry_slab, dic);
|
||||
}
|
||||
|
||||
static void f2fs_put_dic(struct decompress_io_ctx *dic)
|
||||
static void f2fs_late_free_dic(struct work_struct *work)
|
||||
{
|
||||
if (refcount_dec_and_test(&dic->refcnt))
|
||||
f2fs_free_dic(dic);
|
||||
struct decompress_io_ctx *dic =
|
||||
container_of(work, struct decompress_io_ctx, free_work);
|
||||
|
||||
f2fs_free_dic(dic, false);
|
||||
}
|
||||
|
||||
static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
|
||||
{
|
||||
if (refcount_dec_and_test(&dic->refcnt)) {
|
||||
if (in_task) {
|
||||
f2fs_free_dic(dic, false);
|
||||
} else {
|
||||
INIT_WORK(&dic->free_work, f2fs_late_free_dic);
|
||||
queue_work(F2FS_I_SB(dic->inode)->post_read_wq,
|
||||
&dic->free_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update and unlock the cluster's pagecache pages, and release the reference to
|
||||
* the decompress_io_ctx that was being held for I/O completion.
|
||||
*/
|
||||
static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
|
||||
static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
|
||||
bool in_task)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -1667,7 +1740,7 @@ static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
|
||||
unlock_page(rpage);
|
||||
}
|
||||
|
||||
f2fs_put_dic(dic);
|
||||
f2fs_put_dic(dic, in_task);
|
||||
}
|
||||
|
||||
static void f2fs_verify_cluster(struct work_struct *work)
|
||||
@@ -1684,14 +1757,15 @@ static void f2fs_verify_cluster(struct work_struct *work)
|
||||
SetPageError(rpage);
|
||||
}
|
||||
|
||||
__f2fs_decompress_end_io(dic, false);
|
||||
__f2fs_decompress_end_io(dic, false, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when a compressed cluster has been decompressed
|
||||
* (or failed to be read and/or decompressed).
|
||||
*/
|
||||
void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
|
||||
void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
|
||||
bool in_task)
|
||||
{
|
||||
if (!failed && dic->need_verity) {
|
||||
/*
|
||||
@@ -1703,7 +1777,7 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
|
||||
INIT_WORK(&dic->verity_work, f2fs_verify_cluster);
|
||||
fsverity_enqueue_verify_work(&dic->verity_work);
|
||||
} else {
|
||||
__f2fs_decompress_end_io(dic, failed);
|
||||
__f2fs_decompress_end_io(dic, failed, in_task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1712,12 +1786,12 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
|
||||
*
|
||||
* This is called when the page is no longer needed and can be freed.
|
||||
*/
|
||||
void f2fs_put_page_dic(struct page *page)
|
||||
void f2fs_put_page_dic(struct page *page, bool in_task)
|
||||
{
|
||||
struct decompress_io_ctx *dic =
|
||||
(struct decompress_io_ctx *)page_private(page);
|
||||
|
||||
f2fs_put_dic(dic);
|
||||
f2fs_put_dic(dic, in_task);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1907,6 +1981,9 @@ int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi)
|
||||
dev_t dev = sbi->sb->s_bdev->bd_dev;
|
||||
char slab_name[32];
|
||||
|
||||
if (!f2fs_sb_has_compression(sbi))
|
||||
return 0;
|
||||
|
||||
sprintf(slab_name, "f2fs_page_array_entry-%u:%u", MAJOR(dev), MINOR(dev));
|
||||
|
||||
sbi->page_array_slab_size = sizeof(struct page *) <<
|
||||
|
||||
@@ -120,7 +120,7 @@ struct bio_post_read_ctx {
|
||||
block_t fs_blkaddr;
|
||||
};
|
||||
|
||||
static void f2fs_finish_read_bio(struct bio *bio)
|
||||
static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
|
||||
{
|
||||
struct bio_vec *bv;
|
||||
struct bvec_iter_all iter_all;
|
||||
@@ -134,8 +134,9 @@ static void f2fs_finish_read_bio(struct bio *bio)
|
||||
|
||||
if (f2fs_is_compressed_page(page)) {
|
||||
if (bio->bi_status)
|
||||
f2fs_end_read_compressed_page(page, true, 0);
|
||||
f2fs_put_page_dic(page);
|
||||
f2fs_end_read_compressed_page(page, true, 0,
|
||||
in_task);
|
||||
f2fs_put_page_dic(page, in_task);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -192,7 +193,7 @@ static void f2fs_verify_bio(struct work_struct *work)
|
||||
fsverity_verify_bio(bio);
|
||||
}
|
||||
|
||||
f2fs_finish_read_bio(bio);
|
||||
f2fs_finish_read_bio(bio, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -204,7 +205,7 @@ static void f2fs_verify_bio(struct work_struct *work)
|
||||
* can involve reading verity metadata pages from the file, and these verity
|
||||
* metadata pages may be encrypted and/or compressed.
|
||||
*/
|
||||
static void f2fs_verify_and_finish_bio(struct bio *bio)
|
||||
static void f2fs_verify_and_finish_bio(struct bio *bio, bool in_task)
|
||||
{
|
||||
struct bio_post_read_ctx *ctx = bio->bi_private;
|
||||
|
||||
@@ -212,7 +213,7 @@ static void f2fs_verify_and_finish_bio(struct bio *bio)
|
||||
INIT_WORK(&ctx->work, f2fs_verify_bio);
|
||||
fsverity_enqueue_verify_work(&ctx->work);
|
||||
} else {
|
||||
f2fs_finish_read_bio(bio);
|
||||
f2fs_finish_read_bio(bio, in_task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +226,8 @@ static void f2fs_verify_and_finish_bio(struct bio *bio)
|
||||
* that the bio includes at least one compressed page. The actual decompression
|
||||
* is done on a per-cluster basis, not a per-bio basis.
|
||||
*/
|
||||
static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx)
|
||||
static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx,
|
||||
bool in_task)
|
||||
{
|
||||
struct bio_vec *bv;
|
||||
struct bvec_iter_all iter_all;
|
||||
@@ -238,7 +240,7 @@ static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx)
|
||||
/* PG_error was set if decryption failed. */
|
||||
if (f2fs_is_compressed_page(page))
|
||||
f2fs_end_read_compressed_page(page, PageError(page),
|
||||
blkaddr);
|
||||
blkaddr, in_task);
|
||||
else
|
||||
all_compressed = false;
|
||||
|
||||
@@ -263,15 +265,16 @@ static void f2fs_post_read_work(struct work_struct *work)
|
||||
fscrypt_decrypt_bio(ctx->bio);
|
||||
|
||||
if (ctx->enabled_steps & STEP_DECOMPRESS)
|
||||
f2fs_handle_step_decompress(ctx);
|
||||
f2fs_handle_step_decompress(ctx, true);
|
||||
|
||||
f2fs_verify_and_finish_bio(ctx->bio);
|
||||
f2fs_verify_and_finish_bio(ctx->bio, true);
|
||||
}
|
||||
|
||||
static void f2fs_read_end_io(struct bio *bio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(bio_first_page_all(bio));
|
||||
struct bio_post_read_ctx *ctx;
|
||||
bool intask = in_task();
|
||||
|
||||
iostat_update_and_unbind_ctx(bio, 0);
|
||||
ctx = bio->bi_private;
|
||||
@@ -282,16 +285,29 @@ static void f2fs_read_end_io(struct bio *bio)
|
||||
}
|
||||
|
||||
if (bio->bi_status) {
|
||||
f2fs_finish_read_bio(bio);
|
||||
f2fs_finish_read_bio(bio, intask);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx && (ctx->enabled_steps & (STEP_DECRYPT | STEP_DECOMPRESS))) {
|
||||
INIT_WORK(&ctx->work, f2fs_post_read_work);
|
||||
queue_work(ctx->sbi->post_read_wq, &ctx->work);
|
||||
} else {
|
||||
f2fs_verify_and_finish_bio(bio);
|
||||
if (ctx) {
|
||||
unsigned int enabled_steps = ctx->enabled_steps &
|
||||
(STEP_DECRYPT | STEP_DECOMPRESS);
|
||||
|
||||
/*
|
||||
* If we have only decompression step between decompression and
|
||||
* decrypt, we don't need post processing for this.
|
||||
*/
|
||||
if (enabled_steps == STEP_DECOMPRESS &&
|
||||
!f2fs_low_mem_mode(sbi)) {
|
||||
f2fs_handle_step_decompress(ctx, intask);
|
||||
} else if (enabled_steps) {
|
||||
INIT_WORK(&ctx->work, f2fs_post_read_work);
|
||||
queue_work(ctx->sbi->post_read_wq, &ctx->work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_verify_and_finish_bio(bio, intask);
|
||||
}
|
||||
|
||||
static void f2fs_write_end_io(struct bio *bio)
|
||||
@@ -1687,8 +1703,6 @@ sync_out:
|
||||
*/
|
||||
f2fs_wait_on_block_writeback_range(inode,
|
||||
map->m_pblk, map->m_len);
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
map->m_pblk, map->m_pblk);
|
||||
|
||||
if (map->m_multidev_dio) {
|
||||
block_t blk_addr = map->m_pblk;
|
||||
@@ -2240,7 +2254,7 @@ skip_reading_dnode:
|
||||
|
||||
if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
|
||||
if (atomic_dec_and_test(&dic->remaining_pages))
|
||||
f2fs_decompress_cluster(dic);
|
||||
f2fs_decompress_cluster(dic, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2258,7 +2272,7 @@ submit_and_realloc:
|
||||
page->index, for_write);
|
||||
if (IS_ERR(bio)) {
|
||||
ret = PTR_ERR(bio);
|
||||
f2fs_decompress_end_io(dic, ret);
|
||||
f2fs_decompress_end_io(dic, ret, true);
|
||||
f2fs_put_dnode(&dn);
|
||||
*bio_ret = NULL;
|
||||
return ret;
|
||||
@@ -2747,6 +2761,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
.submitted = false,
|
||||
.compr_blocks = compr_blocks,
|
||||
.need_lock = LOCK_RETRY,
|
||||
.post_read = f2fs_post_read_required(inode),
|
||||
.io_type = io_type,
|
||||
.io_wbc = wbc,
|
||||
.bio = bio,
|
||||
@@ -2918,7 +2933,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
|
||||
{
|
||||
int ret = 0;
|
||||
int done = 0, retry = 0;
|
||||
struct pagevec pvec;
|
||||
struct page *pages[F2FS_ONSTACK_PAGES];
|
||||
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
|
||||
struct bio *bio = NULL;
|
||||
sector_t last_block;
|
||||
@@ -2949,8 +2964,6 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
|
||||
int submitted = 0;
|
||||
int i;
|
||||
|
||||
pagevec_init(&pvec);
|
||||
|
||||
if (get_dirty_pages(mapping->host) <=
|
||||
SM_I(F2FS_M_SB(mapping))->min_hot_blocks)
|
||||
set_inode_flag(mapping->host, FI_HOT_DATA);
|
||||
@@ -2976,13 +2989,13 @@ retry:
|
||||
tag_pages_for_writeback(mapping, index, end);
|
||||
done_index = index;
|
||||
while (!done && !retry && (index <= end)) {
|
||||
nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index, end,
|
||||
tag);
|
||||
nr_pages = find_get_pages_range_tag(mapping, &index, end,
|
||||
tag, F2FS_ONSTACK_PAGES, pages);
|
||||
if (nr_pages == 0)
|
||||
break;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
struct page *page = pages[i];
|
||||
bool need_readd;
|
||||
readd:
|
||||
need_readd = false;
|
||||
@@ -3013,6 +3026,10 @@ readd:
|
||||
if (!f2fs_cluster_is_empty(&cc))
|
||||
goto lock_page;
|
||||
|
||||
if (f2fs_all_cluster_page_ready(&cc,
|
||||
pages, i, nr_pages, true))
|
||||
goto lock_page;
|
||||
|
||||
ret2 = f2fs_prepare_compress_overwrite(
|
||||
inode, &pagep,
|
||||
page->index, &fsdata);
|
||||
@@ -3023,8 +3040,8 @@ readd:
|
||||
} else if (ret2 &&
|
||||
(!f2fs_compress_write_end(inode,
|
||||
fsdata, page->index, 1) ||
|
||||
!f2fs_all_cluster_page_loaded(&cc,
|
||||
&pvec, i, nr_pages))) {
|
||||
!f2fs_all_cluster_page_ready(&cc,
|
||||
pages, i, nr_pages, false))) {
|
||||
retry = 1;
|
||||
break;
|
||||
}
|
||||
@@ -3114,7 +3131,7 @@ next:
|
||||
if (need_readd)
|
||||
goto readd;
|
||||
}
|
||||
pagevec_release(&pvec);
|
||||
release_pages(pages, nr_pages);
|
||||
cond_resched();
|
||||
}
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
@@ -3424,12 +3441,11 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
|
||||
struct inode *cow_inode = F2FS_I(inode)->cow_inode;
|
||||
pgoff_t index = page->index;
|
||||
int err = 0;
|
||||
block_t ori_blk_addr;
|
||||
block_t ori_blk_addr = NULL_ADDR;
|
||||
|
||||
/* If pos is beyond the end of file, reserve a new block in COW inode */
|
||||
if ((pos & PAGE_MASK) >= i_size_read(inode))
|
||||
return __reserve_data_block(cow_inode, index, blk_addr,
|
||||
node_changed);
|
||||
goto reserve_block;
|
||||
|
||||
/* Look for the block in COW inode first */
|
||||
err = __find_data_block(cow_inode, index, blk_addr);
|
||||
@@ -3443,10 +3459,12 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
reserve_block:
|
||||
/* Finally, we should reserve a new block in COW inode for the update */
|
||||
err = __reserve_data_block(cow_inode, index, blk_addr, node_changed);
|
||||
if (err)
|
||||
return err;
|
||||
inc_atomic_write_cnt(inode);
|
||||
|
||||
if (ori_blk_addr != NULL_ADDR)
|
||||
*blk_addr = ori_blk_addr;
|
||||
|
||||
@@ -39,7 +39,7 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi)
|
||||
|
||||
bimodal = 0;
|
||||
total_vblocks = 0;
|
||||
blks_per_sec = BLKS_PER_SEC(sbi);
|
||||
blks_per_sec = CAP_BLKS_PER_SEC(sbi);
|
||||
hblks_per_sec = blks_per_sec / 2;
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
|
||||
vblocks = get_valid_blocks(sbi, segno, true);
|
||||
|
||||
102
fs/f2fs/f2fs.h
102
fs/f2fs/f2fs.h
@@ -156,6 +156,7 @@ struct f2fs_mount_info {
|
||||
int fsync_mode; /* fsync policy */
|
||||
int fs_mode; /* fs mode: LFS or ADAPTIVE */
|
||||
int bggc_mode; /* bggc mode: off, on or sync */
|
||||
int memory_mode; /* memory mode */
|
||||
int discard_unit; /*
|
||||
* discard command's offset/size should
|
||||
* be aligned to this unit: block,
|
||||
@@ -226,7 +227,6 @@ enum {
|
||||
#define CP_PAUSE 0x00000040
|
||||
#define CP_RESIZE 0x00000080
|
||||
|
||||
#define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi)
|
||||
#define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */
|
||||
#define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */
|
||||
#define DEF_MID_DISCARD_ISSUE_TIME 500 /* 500 ms, if device busy */
|
||||
@@ -595,6 +595,8 @@ enum {
|
||||
#define RECOVERY_MAX_RA_BLOCKS BIO_MAX_VECS
|
||||
#define RECOVERY_MIN_RA_BLOCKS 1
|
||||
|
||||
#define F2FS_ONSTACK_PAGES 16 /* nr of onstack pages */
|
||||
|
||||
struct rb_entry {
|
||||
struct rb_node rb_node; /* rb node located in rb-tree */
|
||||
union {
|
||||
@@ -754,6 +756,7 @@ enum {
|
||||
FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */
|
||||
FI_COMPRESS_RELEASED, /* compressed blocks were released */
|
||||
FI_ALIGNED_WRITE, /* enable aligned write */
|
||||
FI_COW_FILE, /* indicate COW file */
|
||||
FI_MAX, /* max flag, never be used */
|
||||
};
|
||||
|
||||
@@ -809,6 +812,8 @@ struct f2fs_inode_info {
|
||||
unsigned char i_compress_level; /* compress level (lz4hc,zstd) */
|
||||
unsigned short i_compress_flag; /* compress flag */
|
||||
unsigned int i_cluster_size; /* cluster size */
|
||||
|
||||
unsigned int atomic_write_cnt;
|
||||
};
|
||||
|
||||
static inline void get_extent_info(struct extent_info *ext,
|
||||
@@ -1195,6 +1200,7 @@ struct f2fs_io_info {
|
||||
bool retry; /* need to reallocate block address */
|
||||
int compr_blocks; /* # of compressed block addresses */
|
||||
bool encrypted; /* indicate file is encrypted */
|
||||
bool post_read; /* require post read */
|
||||
enum iostat_type io_type; /* io type */
|
||||
struct writeback_control *io_wbc; /* writeback control */
|
||||
struct bio **bio; /* bio for ipu */
|
||||
@@ -1231,7 +1237,6 @@ struct f2fs_dev_info {
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
unsigned int nr_blkz; /* Total number of zones */
|
||||
unsigned long *blkz_seq; /* Bitmap indicating sequential zones */
|
||||
block_t *zone_capacity_blocks; /* Array of zone capacity in blks */
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1357,6 +1362,13 @@ enum {
|
||||
DISCARD_UNIT_SECTION, /* basic discard unit is section */
|
||||
};
|
||||
|
||||
enum {
|
||||
MEMORY_MODE_NORMAL, /* memory mode for normal devices */
|
||||
MEMORY_MODE_LOW, /* memory mode for low memry devices */
|
||||
};
|
||||
|
||||
|
||||
|
||||
static inline int f2fs_test_bit(unsigned int nr, char *addr);
|
||||
static inline void f2fs_set_bit(unsigned int nr, char *addr);
|
||||
static inline void f2fs_clear_bit(unsigned int nr, char *addr);
|
||||
@@ -1577,6 +1589,7 @@ struct decompress_io_ctx {
|
||||
void *private; /* payload buffer for specified decompression algorithm */
|
||||
void *private2; /* extra payload buffer */
|
||||
struct work_struct verity_work; /* work to verify the decompressed pages */
|
||||
struct work_struct free_work; /* work for late free this structure itself */
|
||||
};
|
||||
|
||||
#define NULL_CLUSTER ((unsigned int)(~0))
|
||||
@@ -1661,6 +1674,7 @@ struct f2fs_sb_info {
|
||||
unsigned int meta_ino_num; /* meta inode number*/
|
||||
unsigned int log_blocks_per_seg; /* log2 blocks per segment */
|
||||
unsigned int blocks_per_seg; /* blocks per segment */
|
||||
unsigned int unusable_blocks_per_sec; /* unusable blocks per section */
|
||||
unsigned int segs_per_sec; /* segments per section */
|
||||
unsigned int secs_per_zone; /* sections per zone */
|
||||
unsigned int total_sections; /* total section count */
|
||||
@@ -1801,6 +1815,12 @@ struct f2fs_sb_info {
|
||||
int max_fragment_chunk; /* max chunk size for block fragmentation mode */
|
||||
int max_fragment_hole; /* max hole size for block fragmentation mode */
|
||||
|
||||
/* For atomic write statistics */
|
||||
atomic64_t current_atomic_write;
|
||||
s64 peak_atomic_write;
|
||||
u64 committed_atomic_block;
|
||||
u64 revoked_atomic_block;
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
struct kmem_cache *page_array_slab; /* page array entry */
|
||||
unsigned int page_array_slab_size; /* default page array slab size */
|
||||
@@ -2415,6 +2435,28 @@ static inline void inode_dec_dirty_pages(struct inode *inode)
|
||||
dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_QDATA);
|
||||
}
|
||||
|
||||
static inline void inc_atomic_write_cnt(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
u64 current_write;
|
||||
|
||||
fi->atomic_write_cnt++;
|
||||
atomic64_inc(&sbi->current_atomic_write);
|
||||
current_write = atomic64_read(&sbi->current_atomic_write);
|
||||
if (current_write > sbi->peak_atomic_write)
|
||||
sbi->peak_atomic_write = current_write;
|
||||
}
|
||||
|
||||
static inline void release_atomic_write_cnt(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
|
||||
atomic64_sub(fi->atomic_write_cnt, &sbi->current_atomic_write);
|
||||
fi->atomic_write_cnt = 0;
|
||||
}
|
||||
|
||||
static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type)
|
||||
{
|
||||
return atomic_read(&sbi->nr_pages[count_type]);
|
||||
@@ -2687,16 +2729,6 @@ static inline struct page *f2fs_pagecache_get_page(
|
||||
return pagecache_get_page(mapping, index, fgp_flags, gfp_mask);
|
||||
}
|
||||
|
||||
static inline void f2fs_copy_page(struct page *src, struct page *dst)
|
||||
{
|
||||
char *src_kaddr = kmap(src);
|
||||
char *dst_kaddr = kmap(dst);
|
||||
|
||||
memcpy(dst_kaddr, src_kaddr, PAGE_SIZE);
|
||||
kunmap(dst);
|
||||
kunmap(src);
|
||||
}
|
||||
|
||||
static inline void f2fs_put_page(struct page *page, int unlock)
|
||||
{
|
||||
if (!page)
|
||||
@@ -3199,6 +3231,11 @@ static inline bool f2fs_is_atomic_file(struct inode *inode)
|
||||
return is_inode_flag_set(inode, FI_ATOMIC_FILE);
|
||||
}
|
||||
|
||||
static inline bool f2fs_is_cow_file(struct inode *inode)
|
||||
{
|
||||
return is_inode_flag_set(inode, FI_COW_FILE);
|
||||
}
|
||||
|
||||
static inline bool f2fs_is_first_block_written(struct inode *inode)
|
||||
{
|
||||
return is_inode_flag_set(inode, FI_FIRST_BLOCK_WRITTEN);
|
||||
@@ -4150,13 +4187,13 @@ void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
|
||||
bool f2fs_is_compress_backend_ready(struct inode *inode);
|
||||
int f2fs_init_compress_mempool(void);
|
||||
void f2fs_destroy_compress_mempool(void);
|
||||
void f2fs_decompress_cluster(struct decompress_io_ctx *dic);
|
||||
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
|
||||
void f2fs_end_read_compressed_page(struct page *page, bool failed,
|
||||
block_t blkaddr);
|
||||
block_t blkaddr, bool in_task);
|
||||
bool f2fs_cluster_is_empty(struct compress_ctx *cc);
|
||||
bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
|
||||
bool f2fs_all_cluster_page_loaded(struct compress_ctx *cc, struct pagevec *pvec,
|
||||
int index, int nr_pages);
|
||||
bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
|
||||
int index, int nr_pages, bool uptodate);
|
||||
bool f2fs_sanity_check_cluster(struct dnode_of_data *dn);
|
||||
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page);
|
||||
int f2fs_write_multi_pages(struct compress_ctx *cc,
|
||||
@@ -4171,8 +4208,9 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
|
||||
unsigned nr_pages, sector_t *last_block_in_bio,
|
||||
bool is_readahead, bool for_write);
|
||||
struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc);
|
||||
void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed);
|
||||
void f2fs_put_page_dic(struct page *page);
|
||||
void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
|
||||
bool in_task);
|
||||
void f2fs_put_page_dic(struct page *page, bool in_task);
|
||||
unsigned int f2fs_cluster_blocks_are_contiguous(struct dnode_of_data *dn);
|
||||
int f2fs_init_compress_ctx(struct compress_ctx *cc);
|
||||
void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse);
|
||||
@@ -4218,13 +4256,14 @@ static inline struct page *f2fs_compress_control_page(struct page *page)
|
||||
}
|
||||
static inline int f2fs_init_compress_mempool(void) { return 0; }
|
||||
static inline void f2fs_destroy_compress_mempool(void) { }
|
||||
static inline void f2fs_decompress_cluster(struct decompress_io_ctx *dic) { }
|
||||
static inline void f2fs_decompress_cluster(struct decompress_io_ctx *dic,
|
||||
bool in_task) { }
|
||||
static inline void f2fs_end_read_compressed_page(struct page *page,
|
||||
bool failed, block_t blkaddr)
|
||||
bool failed, block_t blkaddr, bool in_task)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
static inline void f2fs_put_page_dic(struct page *page)
|
||||
static inline void f2fs_put_page_dic(struct page *page, bool in_task)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
@@ -4250,8 +4289,9 @@ static inline void f2fs_update_extent_tree_range_compressed(struct inode *inode,
|
||||
unsigned int c_len) { }
|
||||
#endif
|
||||
|
||||
static inline void set_compress_context(struct inode *inode)
|
||||
static inline int set_compress_context(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
|
||||
F2FS_I(inode)->i_compress_algorithm =
|
||||
@@ -4274,6 +4314,10 @@ static inline void set_compress_context(struct inode *inode)
|
||||
stat_inc_compr_inode(inode);
|
||||
inc_compr_inode_stat(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool f2fs_disable_compressed_file(struct inode *inode)
|
||||
@@ -4391,10 +4435,15 @@ static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi)
|
||||
return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS;
|
||||
}
|
||||
|
||||
static inline bool f2fs_low_mem_mode(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW;
|
||||
}
|
||||
|
||||
static inline bool f2fs_may_compress(struct inode *inode)
|
||||
{
|
||||
if (IS_SWAPFILE(inode) || f2fs_is_pinned_file(inode) ||
|
||||
f2fs_is_atomic_file(inode))
|
||||
f2fs_is_atomic_file(inode) || f2fs_has_inline_data(inode))
|
||||
return false;
|
||||
return S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode);
|
||||
}
|
||||
@@ -4456,12 +4505,7 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
|
||||
/* disallow direct IO if any of devices has unaligned blksize */
|
||||
if (f2fs_is_multi_device(sbi) && !sbi->aligned_blksize)
|
||||
return true;
|
||||
/*
|
||||
* for blkzoned device, fallback direct IO to buffered IO, so
|
||||
* all IOs can be serialized by log-structured write.
|
||||
*/
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
return true;
|
||||
|
||||
if (f2fs_lfs_mode(sbi) && (rw == WRITE)) {
|
||||
if (block_unaligned_IO(inode, iocb, iter))
|
||||
return true;
|
||||
|
||||
@@ -1279,7 +1279,7 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode,
|
||||
f2fs_put_page(psrc, 1);
|
||||
return PTR_ERR(pdst);
|
||||
}
|
||||
f2fs_copy_page(psrc, pdst);
|
||||
memcpy_page(pdst, 0, psrc, 0, PAGE_SIZE);
|
||||
set_page_dirty(pdst);
|
||||
f2fs_put_page(pdst, 1);
|
||||
f2fs_put_page(psrc, 1);
|
||||
@@ -1682,7 +1682,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
|
||||
return 0;
|
||||
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
block_t sec_blks = BLKS_PER_SEC(sbi);
|
||||
block_t sec_blks = CAP_BLKS_PER_SEC(sbi);
|
||||
block_t sec_len = roundup(map.m_len, sec_blks);
|
||||
|
||||
map.m_len = sec_blks;
|
||||
@@ -1823,8 +1823,7 @@ static int f2fs_release_file(struct inode *inode, struct file *filp)
|
||||
atomic_read(&inode->i_writecount) != 1)
|
||||
return 0;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1838,8 +1837,7 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id)
|
||||
* until all the writers close its file. Since this should be done
|
||||
* before dropping file lock, it needs to do in ->flush.
|
||||
*/
|
||||
if (f2fs_is_atomic_file(inode) &&
|
||||
F2FS_I(inode)->atomic_write_task == current)
|
||||
if (F2FS_I(inode)->atomic_write_task == current)
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
return 0;
|
||||
}
|
||||
@@ -1874,22 +1872,15 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
|
||||
if (masked_flags & F2FS_COMPR_FL) {
|
||||
if (!f2fs_disable_compressed_file(inode))
|
||||
return -EINVAL;
|
||||
}
|
||||
if (iflags & F2FS_NOCOMP_FL)
|
||||
return -EINVAL;
|
||||
if (iflags & F2FS_COMPR_FL) {
|
||||
} else {
|
||||
if (!f2fs_may_compress(inode))
|
||||
return -EINVAL;
|
||||
if (S_ISREG(inode->i_mode) && inode->i_size)
|
||||
if (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))
|
||||
return -EINVAL;
|
||||
|
||||
set_compress_context(inode);
|
||||
if (set_compress_context(inode))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
if ((iflags ^ masked_flags) & F2FS_NOCOMP_FL) {
|
||||
if (masked_flags & F2FS_COMPR_FL)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fi->i_flags = iflags | (fi->i_flags & ~mask);
|
||||
f2fs_bug_on(F2FS_I_SB(inode), (fi->i_flags & F2FS_COMPR_FL) &&
|
||||
@@ -2069,13 +2060,14 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
|
||||
set_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
set_inode_flag(fi->cow_inode, FI_ATOMIC_FILE);
|
||||
set_inode_flag(fi->cow_inode, FI_COW_FILE);
|
||||
clear_inode_flag(fi->cow_inode, FI_INLINE_DATA);
|
||||
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
|
||||
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
fi->atomic_write_task = current;
|
||||
stat_update_max_atomic_write(inode);
|
||||
fi->atomic_write_cnt = 0;
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
mnt_drop_write_file(filp);
|
||||
@@ -2116,6 +2108,30 @@ unlock_out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_abort_atomic_write(struct file *filp)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct user_namespace *mnt_userns = file_mnt_user_ns(filp);
|
||||
int ret;
|
||||
|
||||
if (!inode_owner_or_capable(mnt_userns, inode))
|
||||
return -EACCES;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
|
||||
inode_unlock(inode);
|
||||
|
||||
mnt_drop_write_file(filp);
|
||||
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
@@ -2434,7 +2450,7 @@ do_more:
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
range->start += BLKS_PER_SEC(sbi);
|
||||
range->start += CAP_BLKS_PER_SEC(sbi);
|
||||
if (range->start <= end)
|
||||
goto do_more;
|
||||
out:
|
||||
@@ -2559,7 +2575,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
||||
goto out;
|
||||
}
|
||||
|
||||
sec_num = DIV_ROUND_UP(total, BLKS_PER_SEC(sbi));
|
||||
sec_num = DIV_ROUND_UP(total, CAP_BLKS_PER_SEC(sbi));
|
||||
|
||||
/*
|
||||
* make sure there are enough free section for LFS allocation, this can
|
||||
@@ -3905,10 +3921,10 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
|
||||
|
||||
for (i = 0; i < page_len; i++, redirty_idx++) {
|
||||
page = find_lock_page(mapping, redirty_idx);
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
/* It will never fail, when page has pinned above */
|
||||
f2fs_bug_on(F2FS_I_SB(inode), !page);
|
||||
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
f2fs_put_page(page, 0);
|
||||
@@ -3947,6 +3963,11 @@ static int f2fs_ioc_decompress_file(struct file *filp, unsigned long arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -4014,6 +4035,11 @@ static int f2fs_ioc_compress_file(struct file *filp, unsigned long arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -4062,9 +4088,10 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return f2fs_ioc_start_atomic_write(filp);
|
||||
case F2FS_IOC_COMMIT_ATOMIC_WRITE:
|
||||
return f2fs_ioc_commit_atomic_write(filp);
|
||||
case F2FS_IOC_ABORT_ATOMIC_WRITE:
|
||||
return f2fs_ioc_abort_atomic_write(filp);
|
||||
case F2FS_IOC_START_VOLATILE_WRITE:
|
||||
case F2FS_IOC_RELEASE_VOLATILE_WRITE:
|
||||
case F2FS_IOC_ABORT_VOLATILE_WRITE:
|
||||
return -EOPNOTSUPP;
|
||||
case F2FS_IOC_SHUTDOWN:
|
||||
return f2fs_ioc_shutdown(filp, arg);
|
||||
@@ -4732,7 +4759,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case F2FS_IOC_COMMIT_ATOMIC_WRITE:
|
||||
case F2FS_IOC_START_VOLATILE_WRITE:
|
||||
case F2FS_IOC_RELEASE_VOLATILE_WRITE:
|
||||
case F2FS_IOC_ABORT_VOLATILE_WRITE:
|
||||
case F2FS_IOC_ABORT_ATOMIC_WRITE:
|
||||
case F2FS_IOC_SHUTDOWN:
|
||||
case FITRIM:
|
||||
case FS_IOC_SET_ENCRYPTION_POLICY:
|
||||
|
||||
11
fs/f2fs/gc.c
11
fs/f2fs/gc.c
@@ -150,8 +150,11 @@ do_gc:
|
||||
gc_control.nr_free_secs = foreground ? 1 : 0;
|
||||
|
||||
/* if return value is not zero, no victim was selected */
|
||||
if (f2fs_gc(sbi, &gc_control))
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
if (f2fs_gc(sbi, &gc_control)) {
|
||||
/* don't bother wait_ms by foreground gc */
|
||||
if (!foreground)
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
}
|
||||
|
||||
if (foreground)
|
||||
wake_up_all(&gc_th->fggc_wq);
|
||||
@@ -487,7 +490,7 @@ static void atgc_lookup_victim(struct f2fs_sb_info *sbi,
|
||||
unsigned long long age, u, accu;
|
||||
unsigned long long max_mtime = sit_i->dirty_max_mtime;
|
||||
unsigned long long min_mtime = sit_i->dirty_min_mtime;
|
||||
unsigned int sec_blocks = BLKS_PER_SEC(sbi);
|
||||
unsigned int sec_blocks = CAP_BLKS_PER_SEC(sbi);
|
||||
unsigned int vblocks;
|
||||
unsigned int dirty_threshold = max(am->max_candidate_count,
|
||||
am->candidate_ratio *
|
||||
@@ -1488,7 +1491,7 @@ next_step:
|
||||
*/
|
||||
if ((gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) ||
|
||||
(!force_migrate && get_valid_blocks(sbi, segno, true) ==
|
||||
BLKS_PER_SEC(sbi)))
|
||||
CAP_BLKS_PER_SEC(sbi)))
|
||||
return submitted;
|
||||
|
||||
if (check_valid_map(sbi, segno, off) == 0)
|
||||
|
||||
21
fs/f2fs/gc.h
21
fs/f2fs/gc.h
@@ -120,15 +120,13 @@ static inline block_t free_user_blocks(struct f2fs_sb_info *sbi)
|
||||
return free_blks - ovp_blks;
|
||||
}
|
||||
|
||||
static inline block_t limit_invalid_user_blocks(struct f2fs_sb_info *sbi)
|
||||
static inline block_t limit_invalid_user_blocks(block_t user_block_count)
|
||||
{
|
||||
return (long)(sbi->user_block_count * LIMIT_INVALID_BLOCK) / 100;
|
||||
return (long)(user_block_count * LIMIT_INVALID_BLOCK) / 100;
|
||||
}
|
||||
|
||||
static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
|
||||
static inline block_t limit_free_user_blocks(block_t reclaimable_user_blocks)
|
||||
{
|
||||
block_t reclaimable_user_blocks = sbi->user_block_count -
|
||||
written_block_count(sbi);
|
||||
return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100;
|
||||
}
|
||||
|
||||
@@ -163,15 +161,16 @@ static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
|
||||
static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
block_t invalid_user_blocks = sbi->user_block_count -
|
||||
written_block_count(sbi);
|
||||
block_t user_block_count = sbi->user_block_count;
|
||||
block_t invalid_user_blocks = user_block_count -
|
||||
written_block_count(sbi);
|
||||
/*
|
||||
* Background GC is triggered with the following conditions.
|
||||
* 1. There are a number of invalid blocks.
|
||||
* 2. There is not enough free space.
|
||||
*/
|
||||
if (invalid_user_blocks > limit_invalid_user_blocks(sbi) &&
|
||||
free_user_blocks(sbi) < limit_free_user_blocks(sbi))
|
||||
return true;
|
||||
return false;
|
||||
return (invalid_user_blocks >
|
||||
limit_invalid_user_blocks(user_block_count) &&
|
||||
free_user_blocks(sbi) <
|
||||
limit_free_user_blocks(invalid_user_blocks));
|
||||
}
|
||||
|
||||
@@ -744,8 +744,7 @@ void f2fs_evict_inode(struct inode *inode)
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
|
||||
trace_f2fs_evict_inode(inode);
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
|
||||
@@ -91,8 +91,9 @@ static inline void __record_iostat_latency(struct f2fs_sb_info *sbi)
|
||||
unsigned int cnt;
|
||||
struct f2fs_iostat_latency iostat_lat[MAX_IO_TYPE][NR_PAGE_TYPE];
|
||||
struct iostat_lat_info *io_lat = sbi->iostat_io_lat;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_bh(&sbi->iostat_lat_lock);
|
||||
spin_lock_irqsave(&sbi->iostat_lat_lock, flags);
|
||||
for (idx = 0; idx < MAX_IO_TYPE; idx++) {
|
||||
for (io = 0; io < NR_PAGE_TYPE; io++) {
|
||||
cnt = io_lat->bio_cnt[idx][io];
|
||||
@@ -106,7 +107,7 @@ static inline void __record_iostat_latency(struct f2fs_sb_info *sbi)
|
||||
io_lat->bio_cnt[idx][io] = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&sbi->iostat_lat_lock);
|
||||
spin_unlock_irqrestore(&sbi->iostat_lat_lock, flags);
|
||||
|
||||
trace_f2fs_iostat_latency(sbi, iostat_lat);
|
||||
}
|
||||
@@ -115,14 +116,15 @@ static inline void f2fs_record_iostat(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
unsigned long long iostat_diff[NR_IO_TYPE];
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (time_is_after_jiffies(sbi->iostat_next_period))
|
||||
return;
|
||||
|
||||
/* Need double check under the lock */
|
||||
spin_lock_bh(&sbi->iostat_lock);
|
||||
spin_lock_irqsave(&sbi->iostat_lock, flags);
|
||||
if (time_is_after_jiffies(sbi->iostat_next_period)) {
|
||||
spin_unlock_bh(&sbi->iostat_lock);
|
||||
spin_unlock_irqrestore(&sbi->iostat_lock, flags);
|
||||
return;
|
||||
}
|
||||
sbi->iostat_next_period = jiffies +
|
||||
@@ -133,7 +135,7 @@ static inline void f2fs_record_iostat(struct f2fs_sb_info *sbi)
|
||||
sbi->prev_rw_iostat[i];
|
||||
sbi->prev_rw_iostat[i] = sbi->rw_iostat[i];
|
||||
}
|
||||
spin_unlock_bh(&sbi->iostat_lock);
|
||||
spin_unlock_irqrestore(&sbi->iostat_lock, flags);
|
||||
|
||||
trace_f2fs_iostat(sbi, iostat_diff);
|
||||
|
||||
@@ -145,25 +147,27 @@ void f2fs_reset_iostat(struct f2fs_sb_info *sbi)
|
||||
struct iostat_lat_info *io_lat = sbi->iostat_io_lat;
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&sbi->iostat_lock);
|
||||
spin_lock_irq(&sbi->iostat_lock);
|
||||
for (i = 0; i < NR_IO_TYPE; i++) {
|
||||
sbi->rw_iostat[i] = 0;
|
||||
sbi->prev_rw_iostat[i] = 0;
|
||||
}
|
||||
spin_unlock_bh(&sbi->iostat_lock);
|
||||
spin_unlock_irq(&sbi->iostat_lock);
|
||||
|
||||
spin_lock_bh(&sbi->iostat_lat_lock);
|
||||
spin_lock_irq(&sbi->iostat_lat_lock);
|
||||
memset(io_lat, 0, sizeof(struct iostat_lat_info));
|
||||
spin_unlock_bh(&sbi->iostat_lat_lock);
|
||||
spin_unlock_irq(&sbi->iostat_lat_lock);
|
||||
}
|
||||
|
||||
void f2fs_update_iostat(struct f2fs_sb_info *sbi,
|
||||
enum iostat_type type, unsigned long long io_bytes)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!sbi->iostat_enable)
|
||||
return;
|
||||
|
||||
spin_lock_bh(&sbi->iostat_lock);
|
||||
spin_lock_irqsave(&sbi->iostat_lock, flags);
|
||||
sbi->rw_iostat[type] += io_bytes;
|
||||
|
||||
if (type == APP_BUFFERED_IO || type == APP_DIRECT_IO)
|
||||
@@ -172,7 +176,7 @@ void f2fs_update_iostat(struct f2fs_sb_info *sbi,
|
||||
if (type == APP_BUFFERED_READ_IO || type == APP_DIRECT_READ_IO)
|
||||
sbi->rw_iostat[APP_READ_IO] += io_bytes;
|
||||
|
||||
spin_unlock_bh(&sbi->iostat_lock);
|
||||
spin_unlock_irqrestore(&sbi->iostat_lock, flags);
|
||||
|
||||
f2fs_record_iostat(sbi);
|
||||
}
|
||||
@@ -185,6 +189,7 @@ static inline void __update_iostat_latency(struct bio_iostat_ctx *iostat_ctx,
|
||||
struct f2fs_sb_info *sbi = iostat_ctx->sbi;
|
||||
struct iostat_lat_info *io_lat = sbi->iostat_io_lat;
|
||||
int idx;
|
||||
unsigned long flags;
|
||||
|
||||
if (!sbi->iostat_enable)
|
||||
return;
|
||||
@@ -202,12 +207,12 @@ static inline void __update_iostat_latency(struct bio_iostat_ctx *iostat_ctx,
|
||||
idx = WRITE_ASYNC_IO;
|
||||
}
|
||||
|
||||
spin_lock_bh(&sbi->iostat_lat_lock);
|
||||
spin_lock_irqsave(&sbi->iostat_lat_lock, flags);
|
||||
io_lat->sum_lat[idx][iotype] += ts_diff;
|
||||
io_lat->bio_cnt[idx][iotype]++;
|
||||
if (ts_diff > io_lat->peak_lat[idx][iotype])
|
||||
io_lat->peak_lat[idx][iotype] = ts_diff;
|
||||
spin_unlock_bh(&sbi->iostat_lat_lock);
|
||||
spin_unlock_irqrestore(&sbi->iostat_lat_lock, flags);
|
||||
}
|
||||
|
||||
void iostat_update_and_unbind_ctx(struct bio *bio, int rw)
|
||||
|
||||
@@ -328,6 +328,7 @@ static void set_compress_inode(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
continue;
|
||||
|
||||
/* Do not use inline_data with compression */
|
||||
stat_dec_inline_inode(inode);
|
||||
clear_inode_flag(inode, FI_INLINE_DATA);
|
||||
set_compress_context(inode);
|
||||
return;
|
||||
|
||||
@@ -1292,7 +1292,11 @@ struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs)
|
||||
dec_valid_node_count(sbi, dn->inode, !ofs);
|
||||
goto fail;
|
||||
}
|
||||
f2fs_bug_on(sbi, new_ni.blk_addr != NULL_ADDR);
|
||||
if (unlikely(new_ni.blk_addr != NULL_ADDR)) {
|
||||
err = -EFSCORRUPTED;
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
new_ni.nid = dn->nid;
|
||||
new_ni.ino = dn->inode->i_ino;
|
||||
@@ -1450,7 +1454,9 @@ page_hit:
|
||||
out_err:
|
||||
ClearPageUptodate(page);
|
||||
out_put_err:
|
||||
f2fs_handle_page_eio(sbi, page->index, NODE);
|
||||
/* ENOENT comes from read_node_page which is not an error. */
|
||||
if (err != -ENOENT)
|
||||
f2fs_handle_page_eio(sbi, page->index, NODE);
|
||||
f2fs_put_page(page, 1);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@@ -1943,7 +1949,6 @@ next_step:
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
bool submitted = false;
|
||||
bool may_dirty = true;
|
||||
|
||||
/* give a priority to WB_SYNC threads */
|
||||
if (atomic_read(&sbi->wb_sync_req[NODE]) &&
|
||||
@@ -1996,11 +2001,8 @@ continue_unlock:
|
||||
}
|
||||
|
||||
/* flush dirty inode */
|
||||
if (IS_INODE(page) && may_dirty) {
|
||||
may_dirty = false;
|
||||
if (flush_dirty_inode(page))
|
||||
goto lock_node;
|
||||
}
|
||||
if (IS_INODE(page) && flush_dirty_inode(page))
|
||||
goto lock_node;
|
||||
write_node:
|
||||
f2fs_wait_on_page_writeback(page, NODE, true, true);
|
||||
|
||||
|
||||
@@ -189,18 +189,20 @@ void f2fs_abort_atomic_write(struct inode *inode, bool clean)
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
if (clean)
|
||||
truncate_inode_pages_final(inode->i_mapping);
|
||||
clear_inode_flag(fi->cow_inode, FI_ATOMIC_FILE);
|
||||
iput(fi->cow_inode);
|
||||
fi->cow_inode = NULL;
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
if (!f2fs_is_atomic_file(inode))
|
||||
return;
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
sbi->atomic_files--;
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
}
|
||||
if (clean)
|
||||
truncate_inode_pages_final(inode->i_mapping);
|
||||
clear_inode_flag(fi->cow_inode, FI_COW_FILE);
|
||||
iput(fi->cow_inode);
|
||||
fi->cow_inode = NULL;
|
||||
release_atomic_write_cnt(inode);
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
sbi->atomic_files--;
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
}
|
||||
|
||||
static int __replace_atomic_write_block(struct inode *inode, pgoff_t index,
|
||||
@@ -334,6 +336,11 @@ next:
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
sbi->revoked_atomic_block += fi->atomic_write_cnt;
|
||||
else
|
||||
sbi->committed_atomic_block += fi->atomic_write_cnt;
|
||||
|
||||
__complete_revoke_list(inode, &revoke_list, ret ? true : false);
|
||||
|
||||
return ret;
|
||||
@@ -727,7 +734,7 @@ static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
get_valid_blocks(sbi, segno, true);
|
||||
|
||||
f2fs_bug_on(sbi, unlikely(!valid_blocks ||
|
||||
valid_blocks == BLKS_PER_SEC(sbi)));
|
||||
valid_blocks == CAP_BLKS_PER_SEC(sbi)));
|
||||
|
||||
if (!IS_CURSEC(sbi, secno))
|
||||
set_bit(secno, dirty_i->dirty_secmap);
|
||||
@@ -763,7 +770,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
|
||||
if (!valid_blocks ||
|
||||
valid_blocks == BLKS_PER_SEC(sbi)) {
|
||||
valid_blocks == CAP_BLKS_PER_SEC(sbi)) {
|
||||
clear_bit(secno, dirty_i->dirty_secmap);
|
||||
return;
|
||||
}
|
||||
@@ -3167,7 +3174,7 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
|
||||
return CURSEG_COLD_DATA;
|
||||
if (file_is_hot(inode) ||
|
||||
is_inode_flag_set(inode, FI_HOT_DATA) ||
|
||||
f2fs_is_atomic_file(inode))
|
||||
f2fs_is_cow_file(inode))
|
||||
return CURSEG_HOT_DATA;
|
||||
return f2fs_rw_hint_to_seg_type(inode->i_write_hint);
|
||||
} else {
|
||||
@@ -3434,7 +3441,8 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
|
||||
goto drop_bio;
|
||||
}
|
||||
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
if (fio->post_read)
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
fio->new_blkaddr, fio->new_blkaddr);
|
||||
|
||||
stat_inc_inplace_blocks(fio->sbi);
|
||||
@@ -3617,10 +3625,16 @@ void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr)
|
||||
void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr,
|
||||
block_t len)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
block_t i;
|
||||
|
||||
if (!f2fs_post_read_required(inode))
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
f2fs_wait_on_block_writeback(inode, blkaddr + i);
|
||||
|
||||
invalidate_mapping_pages(META_MAPPING(sbi), blkaddr, blkaddr + len - 1);
|
||||
}
|
||||
|
||||
static int read_compacted_summaries(struct f2fs_sb_info *sbi)
|
||||
@@ -4363,6 +4377,12 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
return err;
|
||||
seg_info_from_raw_sit(se, &sit);
|
||||
|
||||
if (se->type >= NR_PERSISTENT_LOG) {
|
||||
f2fs_err(sbi, "Invalid segment type: %u, segno: %u",
|
||||
se->type, start);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
sit_valid_blocks[SE_PAGETYPE(se)] += se->valid_blocks;
|
||||
|
||||
if (f2fs_block_unit_discard(sbi)) {
|
||||
@@ -4411,6 +4431,13 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
break;
|
||||
seg_info_from_raw_sit(se, &sit);
|
||||
|
||||
if (se->type >= NR_PERSISTENT_LOG) {
|
||||
f2fs_err(sbi, "Invalid segment type: %u, segno: %u",
|
||||
se->type, start);
|
||||
err = -EFSCORRUPTED;
|
||||
break;
|
||||
}
|
||||
|
||||
sit_valid_blocks[SE_PAGETYPE(se)] += se->valid_blocks;
|
||||
|
||||
if (f2fs_block_unit_discard(sbi)) {
|
||||
@@ -4484,7 +4511,6 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int segno = 0, offset = 0, secno;
|
||||
block_t valid_blocks, usable_blks_in_seg;
|
||||
block_t blks_per_sec = BLKS_PER_SEC(sbi);
|
||||
|
||||
while (1) {
|
||||
/* find dirty segment based on free segmap */
|
||||
@@ -4513,7 +4539,7 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
|
||||
valid_blocks = get_valid_blocks(sbi, segno, true);
|
||||
secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
|
||||
if (!valid_blocks || valid_blocks == blks_per_sec)
|
||||
if (!valid_blocks || valid_blocks == CAP_BLKS_PER_SEC(sbi))
|
||||
continue;
|
||||
if (IS_CURSEC(sbi, secno))
|
||||
continue;
|
||||
@@ -4896,7 +4922,7 @@ static unsigned int get_zone_idx(struct f2fs_sb_info *sbi, unsigned int secno,
|
||||
static inline unsigned int f2fs_usable_zone_segs_in_sec(
|
||||
struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
unsigned int dev_idx, zone_idx, unusable_segs_in_sec;
|
||||
unsigned int dev_idx, zone_idx;
|
||||
|
||||
dev_idx = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno));
|
||||
zone_idx = get_zone_idx(sbi, GET_SEC_FROM_SEG(sbi, segno), dev_idx);
|
||||
@@ -4905,18 +4931,12 @@ static inline unsigned int f2fs_usable_zone_segs_in_sec(
|
||||
if (is_conv_zone(sbi, zone_idx, dev_idx))
|
||||
return sbi->segs_per_sec;
|
||||
|
||||
/*
|
||||
* If the zone_capacity_blocks array is NULL, then zone capacity
|
||||
* is equal to the zone size for all zones
|
||||
*/
|
||||
if (!FDEV(dev_idx).zone_capacity_blocks)
|
||||
if (!sbi->unusable_blocks_per_sec)
|
||||
return sbi->segs_per_sec;
|
||||
|
||||
/* Get the segment count beyond zone capacity block */
|
||||
unusable_segs_in_sec = (sbi->blocks_per_blkz -
|
||||
FDEV(dev_idx).zone_capacity_blocks[zone_idx]) >>
|
||||
sbi->log_blocks_per_seg;
|
||||
return sbi->segs_per_sec - unusable_segs_in_sec;
|
||||
return sbi->segs_per_sec - (sbi->unusable_blocks_per_sec >>
|
||||
sbi->log_blocks_per_seg);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4945,12 +4965,11 @@ static inline unsigned int f2fs_usable_zone_blks_in_seg(
|
||||
if (is_conv_zone(sbi, zone_idx, dev_idx))
|
||||
return sbi->blocks_per_seg;
|
||||
|
||||
if (!FDEV(dev_idx).zone_capacity_blocks)
|
||||
if (!sbi->unusable_blocks_per_sec)
|
||||
return sbi->blocks_per_seg;
|
||||
|
||||
sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno));
|
||||
sec_cap_blkaddr = sec_start_blkaddr +
|
||||
FDEV(dev_idx).zone_capacity_blocks[zone_idx];
|
||||
sec_cap_blkaddr = sec_start_blkaddr + CAP_BLKS_PER_SEC(sbi);
|
||||
|
||||
/*
|
||||
* If segment starts before zone capacity and spans beyond
|
||||
|
||||
@@ -101,6 +101,9 @@ static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi,
|
||||
GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
|
||||
#define BLKS_PER_SEC(sbi) \
|
||||
((sbi)->segs_per_sec * (sbi)->blocks_per_seg)
|
||||
#define CAP_BLKS_PER_SEC(sbi) \
|
||||
((sbi)->segs_per_sec * (sbi)->blocks_per_seg - \
|
||||
(sbi)->unusable_blocks_per_sec)
|
||||
#define GET_SEC_FROM_SEG(sbi, segno) \
|
||||
(((segno) == -1) ? -1: (segno) / (sbi)->segs_per_sec)
|
||||
#define GET_SEG_FROM_SEC(sbi, secno) \
|
||||
@@ -609,10 +612,10 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
|
||||
get_pages(sbi, F2FS_DIRTY_DENTS) +
|
||||
get_pages(sbi, F2FS_DIRTY_IMETA);
|
||||
unsigned int total_dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS);
|
||||
unsigned int node_secs = total_node_blocks / BLKS_PER_SEC(sbi);
|
||||
unsigned int dent_secs = total_dent_blocks / BLKS_PER_SEC(sbi);
|
||||
unsigned int node_blocks = total_node_blocks % BLKS_PER_SEC(sbi);
|
||||
unsigned int dent_blocks = total_dent_blocks % BLKS_PER_SEC(sbi);
|
||||
unsigned int node_secs = total_node_blocks / CAP_BLKS_PER_SEC(sbi);
|
||||
unsigned int dent_secs = total_dent_blocks / CAP_BLKS_PER_SEC(sbi);
|
||||
unsigned int node_blocks = total_node_blocks % CAP_BLKS_PER_SEC(sbi);
|
||||
unsigned int dent_blocks = total_dent_blocks % CAP_BLKS_PER_SEC(sbi);
|
||||
unsigned int free, need_lower, need_upper;
|
||||
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/backing-dev.h>
|
||||
@@ -159,6 +160,7 @@ enum {
|
||||
Opt_gc_merge,
|
||||
Opt_nogc_merge,
|
||||
Opt_discard_unit,
|
||||
Opt_memory_mode,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
@@ -235,6 +237,7 @@ static match_table_t f2fs_tokens = {
|
||||
{Opt_gc_merge, "gc_merge"},
|
||||
{Opt_nogc_merge, "nogc_merge"},
|
||||
{Opt_discard_unit, "discard_unit=%s"},
|
||||
{Opt_memory_mode, "memory=%s"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
@@ -499,9 +502,19 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
|
||||
bool is_remount)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
struct fs_parameter param = {
|
||||
.type = fs_value_is_string,
|
||||
.string = arg->from ? arg->from : "",
|
||||
};
|
||||
struct fscrypt_dummy_policy *policy =
|
||||
&F2FS_OPTION(sbi).dummy_enc_policy;
|
||||
int err;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) {
|
||||
f2fs_warn(sbi, "test_dummy_encryption option not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!f2fs_sb_has_encrypt(sbi)) {
|
||||
f2fs_err(sbi, "Encrypt feature is off");
|
||||
return -EINVAL;
|
||||
@@ -513,12 +526,12 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
|
||||
* needed to allow it to be set or changed during remount. We do allow
|
||||
* it to be specified during remount, but only if there is no change.
|
||||
*/
|
||||
if (is_remount && !F2FS_OPTION(sbi).dummy_enc_policy.policy) {
|
||||
if (is_remount && !fscrypt_is_dummy_policy_set(policy)) {
|
||||
f2fs_warn(sbi, "Can't set test_dummy_encryption on remount");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = fscrypt_set_test_dummy_encryption(
|
||||
sb, arg->from, &F2FS_OPTION(sbi).dummy_enc_policy);
|
||||
|
||||
err = fscrypt_parse_test_dummy_encryption(¶m, policy);
|
||||
if (err) {
|
||||
if (err == -EEXIST)
|
||||
f2fs_warn(sbi,
|
||||
@@ -531,12 +544,14 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
|
||||
opt, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = fscrypt_add_test_dummy_key(sb, policy);
|
||||
if (err) {
|
||||
f2fs_warn(sbi, "Error adding test dummy encryption key [%d]",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
f2fs_warn(sbi, "Test dummy encryption mode enabled");
|
||||
return 0;
|
||||
#else
|
||||
f2fs_warn(sbi, "test_dummy_encryption option not supported");
|
||||
return -EINVAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
@@ -1229,6 +1244,22 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
}
|
||||
kfree(name);
|
||||
break;
|
||||
case Opt_memory_mode:
|
||||
name = match_strdup(&args[0]);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (!strcmp(name, "normal")) {
|
||||
F2FS_OPTION(sbi).memory_mode =
|
||||
MEMORY_MODE_NORMAL;
|
||||
} else if (!strcmp(name, "low")) {
|
||||
F2FS_OPTION(sbi).memory_mode =
|
||||
MEMORY_MODE_LOW;
|
||||
} else {
|
||||
kfree(name);
|
||||
return -EINVAL;
|
||||
}
|
||||
kfree(name);
|
||||
break;
|
||||
default:
|
||||
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
|
||||
p);
|
||||
@@ -1383,8 +1414,7 @@ static int f2fs_drop_inode(struct inode *inode)
|
||||
atomic_inc(&inode->i_count);
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
|
||||
/* should remain fi->extent_tree for writepage */
|
||||
f2fs_destroy_extent_node(inode);
|
||||
@@ -1494,7 +1524,6 @@ static void destroy_device_list(struct f2fs_sb_info *sbi)
|
||||
blkdev_put(FDEV(i).bdev, FMODE_EXCL);
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
kvfree(FDEV(i).blkz_seq);
|
||||
kfree(FDEV(i).zone_capacity_blocks);
|
||||
#endif
|
||||
}
|
||||
kvfree(sbi->devs);
|
||||
@@ -1996,6 +2025,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
else if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SECTION)
|
||||
seq_printf(seq, ",discard_unit=%s", "section");
|
||||
|
||||
if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_NORMAL)
|
||||
seq_printf(seq, ",memory=%s", "normal");
|
||||
else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW)
|
||||
seq_printf(seq, ",memory=%s", "low");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2017,6 +2051,7 @@ static void default_options(struct f2fs_sb_info *sbi)
|
||||
F2FS_OPTION(sbi).compress_ext_cnt = 0;
|
||||
F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
|
||||
F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL;
|
||||
|
||||
sbi->sb->s_flags &= ~SB_INLINECRYPT;
|
||||
|
||||
@@ -3583,6 +3618,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
|
||||
sbi->max_fragment_chunk = DEF_FRAGMENT_SIZE;
|
||||
sbi->max_fragment_hole = DEF_FRAGMENT_SIZE;
|
||||
spin_lock_init(&sbi->gc_urgent_high_lock);
|
||||
atomic64_set(&sbi->current_atomic_write, 0);
|
||||
|
||||
sbi->dir_level = DEF_DIR_LEVEL;
|
||||
sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
|
||||
@@ -3640,24 +3676,29 @@ err_valid_block:
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
|
||||
struct f2fs_report_zones_args {
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct f2fs_dev_info *dev;
|
||||
bool zone_cap_mismatch;
|
||||
};
|
||||
|
||||
static int f2fs_report_zone_cb(struct blk_zone *zone, unsigned int idx,
|
||||
void *data)
|
||||
{
|
||||
struct f2fs_report_zones_args *rz_args = data;
|
||||
block_t unusable_blocks = (zone->len - zone->capacity) >>
|
||||
F2FS_LOG_SECTORS_PER_BLOCK;
|
||||
|
||||
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
|
||||
return 0;
|
||||
|
||||
set_bit(idx, rz_args->dev->blkz_seq);
|
||||
rz_args->dev->zone_capacity_blocks[idx] = zone->capacity >>
|
||||
F2FS_LOG_SECTORS_PER_BLOCK;
|
||||
if (zone->len != zone->capacity && !rz_args->zone_cap_mismatch)
|
||||
rz_args->zone_cap_mismatch = true;
|
||||
|
||||
if (!rz_args->sbi->unusable_blocks_per_sec) {
|
||||
rz_args->sbi->unusable_blocks_per_sec = unusable_blocks;
|
||||
return 0;
|
||||
}
|
||||
if (rz_args->sbi->unusable_blocks_per_sec != unusable_blocks) {
|
||||
f2fs_err(rz_args->sbi, "F2FS supports single zone capacity\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3698,26 +3739,13 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
|
||||
if (!FDEV(devi).blkz_seq)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get block zones type and zone-capacity */
|
||||
FDEV(devi).zone_capacity_blocks = f2fs_kzalloc(sbi,
|
||||
FDEV(devi).nr_blkz * sizeof(block_t),
|
||||
GFP_KERNEL);
|
||||
if (!FDEV(devi).zone_capacity_blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
rep_zone_arg.sbi = sbi;
|
||||
rep_zone_arg.dev = &FDEV(devi);
|
||||
rep_zone_arg.zone_cap_mismatch = false;
|
||||
|
||||
ret = blkdev_report_zones(bdev, 0, BLK_ALL_ZONES, f2fs_report_zone_cb,
|
||||
&rep_zone_arg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!rep_zone_arg.zone_cap_mismatch) {
|
||||
kfree(FDEV(devi).zone_capacity_blocks);
|
||||
FDEV(devi).zone_capacity_blocks = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -340,6 +340,21 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||
sbi->gc_reclaimed_segs[sbi->gc_segment_mode]);
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "current_atomic_write")) {
|
||||
s64 current_write = atomic64_read(&sbi->current_atomic_write);
|
||||
|
||||
return sysfs_emit(buf, "%lld\n", current_write);
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "peak_atomic_write"))
|
||||
return sysfs_emit(buf, "%lld\n", sbi->peak_atomic_write);
|
||||
|
||||
if (!strcmp(a->attr.name, "committed_atomic_block"))
|
||||
return sysfs_emit(buf, "%llu\n", sbi->committed_atomic_block);
|
||||
|
||||
if (!strcmp(a->attr.name, "revoked_atomic_block"))
|
||||
return sysfs_emit(buf, "%llu\n", sbi->revoked_atomic_block);
|
||||
|
||||
ui = (unsigned int *)(ptr + a->offset);
|
||||
|
||||
return sprintf(buf, "%u\n", *ui);
|
||||
@@ -609,6 +624,27 @@ out:
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "peak_atomic_write")) {
|
||||
if (t != 0)
|
||||
return -EINVAL;
|
||||
sbi->peak_atomic_write = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "committed_atomic_block")) {
|
||||
if (t != 0)
|
||||
return -EINVAL;
|
||||
sbi->committed_atomic_block = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "revoked_atomic_block")) {
|
||||
if (t != 0)
|
||||
return -EINVAL;
|
||||
sbi->revoked_atomic_block = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
*ui = (unsigned int)t;
|
||||
|
||||
return count;
|
||||
@@ -714,6 +750,11 @@ static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
.offset = _offset \
|
||||
}
|
||||
|
||||
#define F2FS_RO_ATTR(struct_type, struct_name, name, elname) \
|
||||
F2FS_ATTR_OFFSET(struct_type, name, 0444, \
|
||||
f2fs_sbi_show, NULL, \
|
||||
offsetof(struct struct_name, elname))
|
||||
|
||||
#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \
|
||||
F2FS_ATTR_OFFSET(struct_type, name, 0644, \
|
||||
f2fs_sbi_show, f2fs_sbi_store, \
|
||||
@@ -812,6 +853,8 @@ F2FS_FEATURE_RO_ATTR(encrypted_casefold);
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
F2FS_FEATURE_RO_ATTR(block_zoned);
|
||||
F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, unusable_blocks_per_sec,
|
||||
unusable_blocks_per_sec);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(atomic_write);
|
||||
F2FS_FEATURE_RO_ATTR(extra_attr);
|
||||
@@ -849,6 +892,12 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_reclaimed_segments, gc_reclaimed_segs);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_chunk, max_fragment_chunk);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_hole, max_fragment_hole);
|
||||
|
||||
/* For atomic write */
|
||||
F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, current_atomic_write, current_atomic_write);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, peak_atomic_write, peak_atomic_write);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, committed_atomic_block, committed_atomic_block);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, revoked_atomic_block, revoked_atomic_block);
|
||||
|
||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||
static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(gc_urgent_sleep_time),
|
||||
@@ -920,6 +969,9 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(moved_blocks_background),
|
||||
ATTR_LIST(avg_vblocks),
|
||||
#endif
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
ATTR_LIST(unusable_blocks_per_sec),
|
||||
#endif
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
ATTR_LIST(compr_written_block),
|
||||
ATTR_LIST(compr_saved_block),
|
||||
@@ -935,6 +987,10 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(gc_reclaimed_segments),
|
||||
ATTR_LIST(max_fragment_chunk),
|
||||
ATTR_LIST(max_fragment_hole),
|
||||
ATTR_LIST(current_atomic_write),
|
||||
ATTR_LIST(peak_atomic_write),
|
||||
ATTR_LIST(committed_atomic_block),
|
||||
ATTR_LIST(revoked_atomic_block),
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(f2fs);
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
#define WORST_COMPR_FACTOR 2
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
#define UBIFS_CIPHER_BLOCK_SIZE FS_CRYPTO_BLOCK_SIZE
|
||||
#define UBIFS_CIPHER_BLOCK_SIZE FSCRYPT_CONTENTS_ALIGNMENT
|
||||
#else
|
||||
#define UBIFS_CIPHER_BLOCK_SIZE 0
|
||||
#endif
|
||||
|
||||
@@ -18,10 +18,21 @@
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/fscrypt.h>
|
||||
|
||||
#define FS_CRYPTO_BLOCK_SIZE 16
|
||||
/*
|
||||
* The lengths of all file contents blocks must be divisible by this value.
|
||||
* This is needed to ensure that all contents encryption modes will work, as
|
||||
* some of the supported modes don't support arbitrarily byte-aligned messages.
|
||||
*
|
||||
* Since the needed alignment is 16 bytes, most filesystems will meet this
|
||||
* requirement naturally, as typical block sizes are powers of 2. However, if a
|
||||
* filesystem can generate arbitrarily byte-aligned block lengths (e.g., via
|
||||
* compression), then it will need to pad to this alignment before encryption.
|
||||
*/
|
||||
#define FSCRYPT_CONTENTS_ALIGNMENT 16
|
||||
|
||||
union fscrypt_policy;
|
||||
struct fscrypt_info;
|
||||
struct fs_parameter;
|
||||
struct seq_file;
|
||||
|
||||
struct fscrypt_str {
|
||||
@@ -286,10 +297,19 @@ struct fscrypt_dummy_policy {
|
||||
const union fscrypt_policy *policy;
|
||||
};
|
||||
|
||||
int fscrypt_parse_test_dummy_encryption(const struct fs_parameter *param,
|
||||
struct fscrypt_dummy_policy *dummy_policy);
|
||||
bool fscrypt_dummy_policies_equal(const struct fscrypt_dummy_policy *p1,
|
||||
const struct fscrypt_dummy_policy *p2);
|
||||
int fscrypt_set_test_dummy_encryption(struct super_block *sb, const char *arg,
|
||||
struct fscrypt_dummy_policy *dummy_policy);
|
||||
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
|
||||
struct super_block *sb);
|
||||
static inline bool
|
||||
fscrypt_is_dummy_policy_set(const struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
return dummy_policy->policy != NULL;
|
||||
}
|
||||
static inline void
|
||||
fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
@@ -300,6 +320,8 @@ fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy)
|
||||
/* keyring.c */
|
||||
void fscrypt_sb_free(struct super_block *sb);
|
||||
int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
|
||||
int fscrypt_add_test_dummy_key(struct super_block *sb,
|
||||
const struct fscrypt_dummy_policy *dummy_policy);
|
||||
int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
|
||||
int fscrypt_ioctl_remove_key_all_users(struct file *filp, void __user *arg);
|
||||
int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
|
||||
@@ -474,12 +496,32 @@ static inline int fscrypt_set_context(struct inode *inode, void *fs_data)
|
||||
struct fscrypt_dummy_policy {
|
||||
};
|
||||
|
||||
static inline int
|
||||
fscrypt_parse_test_dummy_encryption(const struct fs_parameter *param,
|
||||
struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fscrypt_dummy_policies_equal(const struct fscrypt_dummy_policy *p1,
|
||||
const struct fscrypt_dummy_policy *p2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
|
||||
char sep,
|
||||
struct super_block *sb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fscrypt_is_dummy_policy_set(const struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
@@ -495,6 +537,13 @@ static inline int fscrypt_ioctl_add_key(struct file *filp, void __user *arg)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int
|
||||
fscrypt_add_test_dummy_key(struct super_block *sb,
|
||||
const struct fscrypt_dummy_policy *dummy_policy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2)
|
||||
#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
|
||||
#define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4)
|
||||
#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
|
||||
#define F2FS_IOC_ABORT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
|
||||
#define F2FS_IOC_GARBAGE_COLLECT _IOW(F2FS_IOCTL_MAGIC, 6, __u32)
|
||||
#define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7)
|
||||
#define F2FS_IOC_DEFRAGMENT _IOWR(F2FS_IOCTL_MAGIC, 8, \
|
||||
|
||||
Reference in New Issue
Block a user