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:
Jaegeuk Kim
2022-08-09 09:04:34 -07:00
27 changed files with 822 additions and 366 deletions

View File

@@ -580,3 +580,33 @@ Date: January 2022
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org> Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description: Controls max # of node block writes to be used for roll forward Description: Controls max # of node block writes to be used for roll forward
recovery. This can limit the roll forward recovery time. 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".

View File

@@ -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 default, it is helpful for large sized SMR or ZNS devices to
reduce memory cost by getting rid of fs metadata supports small reduce memory cost by getting rid of fs metadata supports small
discard. 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 Debugfs Entries

View File

@@ -113,7 +113,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
if (WARN_ON_ONCE(len <= 0)) if (WARN_ON_ONCE(len <= 0))
return -EINVAL; return -EINVAL;
if (WARN_ON_ONCE(len % FS_CRYPTO_BLOCK_SIZE != 0)) if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0))
return -EINVAL; return -EINVAL;
fscrypt_generate_iv(&iv, lblk_num, ci); 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 * fscrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place
* @inode: The inode to which this block belongs * @inode: The inode to which this block belongs
* @page: The page containing the block to encrypt * @page: The page containing the block to encrypt
* @len: Size of block to encrypt. Doesn't need to be a multiple of the * @len: Size of block to encrypt. This must be a multiple of
* fs block size, but must be a multiple of FS_CRYPTO_BLOCK_SIZE. * FSCRYPT_CONTENTS_ALIGNMENT.
* @offs: Byte offset within @page at which the block to encrypt begins * @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 * @lblk_num: Filesystem logical block number of the block, i.e. the 0-based
* number of the block within the file * 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 * fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place
* @inode: The inode to which this block belongs * @inode: The inode to which this block belongs
* @page: The page containing the block to decrypt * @page: The page containing the block to decrypt
* @len: Size of block to decrypt. Doesn't need to be a multiple of the * @len: Size of block to decrypt. This must be a multiple of
* fs block size, but must be a multiple of FS_CRYPTO_BLOCK_SIZE. * FSCRYPT_CONTENTS_ALIGNMENT.
* @offs: Byte offset within @page at which the block to decrypt begins * @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 * @lblk_num: Filesystem logical block number of the block, i.e. the 0-based
* number of the block within the file * number of the block within the file

View File

@@ -18,6 +18,13 @@
#include <crypto/skcipher.h> #include <crypto/skcipher.h>
#include "fscrypt_private.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 * 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) if (orig_len > max_len)
return false; 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 = round_up(encrypted_len, padding);
*encrypted_len_ret = min(encrypted_len, max_len); *encrypted_len_ret = min(encrypted_len, max_len);
return true; return true;
@@ -350,7 +357,7 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode,
return 0; return 0;
} }
if (iname->len < FS_CRYPTO_BLOCK_SIZE) if (iname->len < FSCRYPT_FNAME_MIN_MSG_LEN)
return -EUCLEAN; return -EUCLEAN;
if (fscrypt_has_encryption_key(inode)) if (fscrypt_has_encryption_key(inode))

View File

@@ -602,8 +602,8 @@ struct key *
fscrypt_find_master_key(struct super_block *sb, fscrypt_find_master_key(struct super_block *sb,
const struct fscrypt_key_specifier *mk_spec); const struct fscrypt_key_specifier *mk_spec);
int fscrypt_add_test_dummy_key(struct super_block *sb, int fscrypt_get_test_dummy_key_identifier(
struct fscrypt_key_specifier *key_spec); u8 key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
int fscrypt_verify_key_added(struct super_block *sb, int fscrypt_verify_key_added(struct super_block *sb,
const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]); const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
@@ -618,7 +618,9 @@ struct fscrypt_mode {
int keysize; /* key size in bytes */ int keysize; /* key size in bytes */
int security_strength; /* security strength in bytes */ int security_strength; /* security strength in bytes */
int ivsize; /* IV size 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; 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, bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
const union fscrypt_policy *policy2); 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, bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
const struct inode *inode); const struct inode *inode);
int fscrypt_policy_from_context(union fscrypt_policy *policy_u, int fscrypt_policy_from_context(union fscrypt_policy *policy_u,

View File

@@ -12,7 +12,6 @@
* provides the key and IV to use. * provides the key and IV to use.
*/ */
#include <linux/blk-crypto.h>
#include <linux/blk-crypto-profile.h> #include <linux/blk-crypto-profile.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/buffer_head.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); 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. */ /* Enable inline encryption for this file if supported. */
int fscrypt_select_encryption_impl(struct fscrypt_info *ci, int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
bool is_hw_wrapped_key) bool is_hw_wrapped_key)
@@ -122,6 +150,8 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci,
goto out_free_devs; goto out_free_devs;
} }
fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg);
ci->ci_inlinecrypt = true; ci->ci_inlinecrypt = true;
out_free_devs: out_free_devs:
kfree(devs); kfree(devs);

View File

@@ -719,29 +719,69 @@ out_wipe_secret:
} }
EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
/* static void
* Add the key for '-o test_dummy_encryption' to the filesystem keyring. fscrypt_get_test_dummy_secret(struct fscrypt_master_key_secret *secret)
*
* 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 u8 test_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; 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); get_random_once(test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE);
memset(&secret, 0, sizeof(secret)); memset(secret, 0, sizeof(*secret));
secret.size = FSCRYPT_MAX_STANDARD_KEY_SIZE; secret->size = FSCRYPT_MAX_STANDARD_KEY_SIZE;
memcpy(secret.raw, test_key, 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); wipe_master_key_secret(&secret);
return err; 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 * 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 * (returns -ENOKEY if not). This is needed to prevent a user from encrypting

View File

@@ -94,7 +94,7 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
mode->cipher_str, PTR_ERR(tfm)); mode->cipher_str, PTR_ERR(tfm));
return 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 * fscrypt performance can vary greatly depending on which
* crypto algorithm implementation is used. Help people debug * 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; struct fscrypt_key_specifier mk_spec;
int err; int err;
switch (ci->ci_policy.version) { err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);
case FSCRYPT_POLICY_V1: if (err)
mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; return err;
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;
}
key = fscrypt_find_master_key(ci->ci_inode->i_sb, &mk_spec); key = fscrypt_find_master_key(ci->ci_inode->i_sb, &mk_spec);
if (IS_ERR(key)) { if (IS_ERR(key)) {

View File

@@ -10,6 +10,7 @@
* Modified by Eric Biggers, 2019 for v2 policy support. * Modified by Eric Biggers, 2019 for v2 policy support.
*/ */
#include <linux/fs_context.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/string.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)); 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 * static const union fscrypt_policy *
fscrypt_get_dummy_policy(struct super_block *sb) 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); EXPORT_SYMBOL_GPL(fscrypt_set_context);
/** /**
* fscrypt_set_test_dummy_encryption() - handle '-o test_dummy_encryption' * fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option
* @sb: the filesystem on which test_dummy_encryption is being specified * @param: the mount option
* @arg: the argument to the test_dummy_encryption option. May be NULL. * @dummy_policy: (input/output) the place to write the dummy policy that will
* @dummy_policy: the filesystem's current dummy policy (input/output, see * result from parsing the option. Zero-initialize this. If a policy is
* below) * 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 * Return: 0 on success; -EINVAL if the argument is invalid; -EEXIST if the
* policy, saving it in @dummy_policy, and adding the corresponding dummy * argument conflicts with one already specified; or -ENOMEM.
* 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.
*/ */
int fscrypt_set_test_dummy_encryption(struct super_block *sb, const char *arg, int fscrypt_parse_test_dummy_encryption(const struct fs_parameter *param,
struct fscrypt_dummy_policy *dummy_policy) struct fscrypt_dummy_policy *dummy_policy)
{ {
struct fscrypt_key_specifier key_spec = { 0 }; const char *arg = "v2";
int version; union fscrypt_policy *policy;
union fscrypt_policy *policy = NULL;
int err; int err;
if (!arg) if (param->type == fs_value_is_string && *param->string)
arg = "v2"; arg = param->string;
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;
}
policy = kzalloc(sizeof(*policy), GFP_KERNEL); policy = kzalloc(sizeof(*policy), GFP_KERNEL);
if (!policy) { if (!policy)
err = -ENOMEM; return -ENOMEM;
goto out;
}
err = fscrypt_add_test_dummy_key(sb, &key_spec); if (!strcmp(arg, "v1")) {
if (err) policy->version = FSCRYPT_POLICY_V1;
goto out;
policy->version = version;
switch (policy->version) {
case FSCRYPT_POLICY_V1:
policy->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; policy->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
policy->v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; 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); FSCRYPT_KEY_DESCRIPTOR_SIZE);
break; } else if (!strcmp(arg, "v2")) {
case FSCRYPT_POLICY_V2: policy->version = FSCRYPT_POLICY_V2;
policy->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; policy->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
policy->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; policy->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memcpy(policy->v2.master_key_identifier, key_spec.u.identifier, err = fscrypt_get_test_dummy_key_identifier(
FSCRYPT_KEY_IDENTIFIER_SIZE); policy->v2.master_key_identifier);
break; if (err)
default: goto out;
WARN_ON(1); } else {
err = -EINVAL; err = -EINVAL;
goto out; goto out;
} }
@@ -789,6 +782,37 @@ out:
kfree(policy); kfree(policy);
return err; 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(&param, dummy_policy) ?:
fscrypt_add_test_dummy_key(sb, dummy_policy);
}
EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption); EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption);
/** /**

View File

@@ -728,14 +728,19 @@ out:
return ret; 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_sb_info *sbi = F2FS_I_SB(dic->inode);
struct f2fs_inode_info *fi = F2FS_I(dic->inode); struct f2fs_inode_info *fi = F2FS_I(dic->inode);
const struct f2fs_compress_ops *cops = const struct f2fs_compress_ops *cops =
f2fs_cops[fi->i_compress_algorithm]; f2fs_cops[fi->i_compress_algorithm];
bool bypass_callback = false;
int ret; int ret;
int i;
trace_f2fs_decompress_pages_start(dic->inode, dic->cluster_idx, trace_f2fs_decompress_pages_start(dic->inode, dic->cluster_idx,
dic->cluster_size, fi->i_compress_algorithm); dic->cluster_size, fi->i_compress_algorithm);
@@ -745,41 +750,10 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
goto out_end_io; goto out_end_io;
} }
dic->tpages = page_array_alloc(dic->inode, dic->cluster_size); ret = f2fs_prepare_decomp_mem(dic, false);
if (!dic->tpages) { if (ret) {
ret = -ENOMEM; bypass_callback = true;
goto out_end_io; goto out_release;
}
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;
} }
dic->clen = le32_to_cpu(dic->cbuf->clen); 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) { if (dic->clen > PAGE_SIZE * dic->nr_cpages - COMPRESS_HEADER_SIZE) {
ret = -EFSCORRUPTED; ret = -EFSCORRUPTED;
goto out_vunmap_cbuf; goto out_release;
} }
ret = cops->decompress_pages(dic); ret = cops->decompress_pages(dic);
@@ -808,17 +782,13 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
} }
} }
out_vunmap_cbuf: out_release:
vm_unmap_ram(dic->cbuf, dic->nr_cpages); f2fs_release_decomp_mem(dic, bypass_callback, false);
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_end_io: out_end_io:
trace_f2fs_decompress_pages_end(dic->inode, dic->cluster_idx, trace_f2fs_decompress_pages_end(dic->inode, dic->cluster_idx,
dic->clen, ret); 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). * (or in the case of a failure, cleans up without actually decompressing).
*/ */
void f2fs_end_read_compressed_page(struct page *page, bool failed, 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 *dic =
(struct decompress_io_ctx *)page_private(page); (struct decompress_io_ctx *)page_private(page);
@@ -838,12 +808,12 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed,
if (failed) if (failed)
WRITE_ONCE(dic->failed, true); WRITE_ONCE(dic->failed, true);
else if (blkaddr) else if (blkaddr && in_task)
f2fs_cache_compressed_page(sbi, page, f2fs_cache_compressed_page(sbi, page,
dic->inode->i_ino, blkaddr); dic->inode->i_ino, blkaddr);
if (atomic_dec_and_test(&dic->remaining_pages)) 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) 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); return is_page_in_cluster(cc, index);
} }
bool f2fs_all_cluster_page_loaded(struct compress_ctx *cc, struct pagevec *pvec, bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
int index, int nr_pages) int index, int nr_pages, bool uptodate)
{ {
unsigned long pgidx; unsigned long pgidx = pages[index]->index;
int i; 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) if (nr_pages - index < cc->cluster_size)
return false; return false;
pgidx = pvec->pages[index]->index; for (; i < cc->cluster_size; i++) {
if (pages[index + i]->index != pgidx + i)
for (i = 1; i < cc->cluster_size; i++) { return false;
if (pvec->pages[index + i]->index != pgidx + i) if (uptodate && !PageUptodate(pages[index + i]))
return false; return false;
} }
@@ -1551,16 +1528,85 @@ destroy_out:
return err; 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 *f2fs_alloc_dic(struct compress_ctx *cc)
{ {
struct decompress_io_ctx *dic; struct decompress_io_ctx *dic;
pgoff_t start_idx = start_idx_of_cluster(cc); 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, dic = f2fs_kmem_cache_alloc(dic_entry_slab, GFP_F2FS_ZERO, false, sbi);
false, F2FS_I_SB(cc->inode));
if (!dic) if (!dic)
return ERR_PTR(-ENOMEM); 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->nr_rpages = cc->cluster_size;
dic->cpages = page_array_alloc(dic->inode, dic->nr_cpages); dic->cpages = page_array_alloc(dic->inode, dic->nr_cpages);
if (!dic->cpages) if (!dic->cpages) {
ret = -ENOMEM;
goto out_free; goto out_free;
}
for (i = 0; i < dic->nr_cpages; i++) { for (i = 0; i < dic->nr_cpages; i++) {
struct page *page; struct page *page;
page = f2fs_compress_alloc_page(); page = f2fs_compress_alloc_page();
if (!page) if (!page) {
ret = -ENOMEM;
goto out_free; goto out_free;
}
f2fs_set_compressed_page(page, cc->inode, f2fs_set_compressed_page(page, cc->inode,
start_idx + i + 1, dic); start_idx + i + 1, dic);
dic->cpages[i] = page; dic->cpages[i] = page;
} }
ret = f2fs_prepare_decomp_mem(dic, true);
if (ret)
goto out_free;
return dic; return dic;
out_free: out_free:
f2fs_free_dic(dic); f2fs_free_dic(dic, true);
return ERR_PTR(-ENOMEM); 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; int i;
f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
if (dic->tpages) { if (dic->tpages) {
for (i = 0; i < dic->cluster_size; i++) { for (i = 0; i < dic->cluster_size; i++) {
if (dic->rpages[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); 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)) struct decompress_io_ctx *dic =
f2fs_free_dic(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 * 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. * 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; int i;
@@ -1667,7 +1740,7 @@ static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
unlock_page(rpage); unlock_page(rpage);
} }
f2fs_put_dic(dic); f2fs_put_dic(dic, in_task);
} }
static void f2fs_verify_cluster(struct work_struct *work) static void f2fs_verify_cluster(struct work_struct *work)
@@ -1684,14 +1757,15 @@ static void f2fs_verify_cluster(struct work_struct *work)
SetPageError(rpage); 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 * This is called when a compressed cluster has been decompressed
* (or failed to be read and/or 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) { 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); INIT_WORK(&dic->verity_work, f2fs_verify_cluster);
fsverity_enqueue_verify_work(&dic->verity_work); fsverity_enqueue_verify_work(&dic->verity_work);
} else { } 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. * 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 *dic =
(struct decompress_io_ctx *)page_private(page); (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; dev_t dev = sbi->sb->s_bdev->bd_dev;
char slab_name[32]; 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)); sprintf(slab_name, "f2fs_page_array_entry-%u:%u", MAJOR(dev), MINOR(dev));
sbi->page_array_slab_size = sizeof(struct page *) << sbi->page_array_slab_size = sizeof(struct page *) <<

View File

@@ -120,7 +120,7 @@ struct bio_post_read_ctx {
block_t fs_blkaddr; 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 bio_vec *bv;
struct bvec_iter_all iter_all; 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 (f2fs_is_compressed_page(page)) {
if (bio->bi_status) if (bio->bi_status)
f2fs_end_read_compressed_page(page, true, 0); f2fs_end_read_compressed_page(page, true, 0,
f2fs_put_page_dic(page); in_task);
f2fs_put_page_dic(page, in_task);
continue; continue;
} }
@@ -192,7 +193,7 @@ static void f2fs_verify_bio(struct work_struct *work)
fsverity_verify_bio(bio); 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 * can involve reading verity metadata pages from the file, and these verity
* metadata pages may be encrypted and/or compressed. * 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; 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); INIT_WORK(&ctx->work, f2fs_verify_bio);
fsverity_enqueue_verify_work(&ctx->work); fsverity_enqueue_verify_work(&ctx->work);
} else { } 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 * that the bio includes at least one compressed page. The actual decompression
* is done on a per-cluster basis, not a per-bio basis. * 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 bio_vec *bv;
struct bvec_iter_all iter_all; 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. */ /* PG_error was set if decryption failed. */
if (f2fs_is_compressed_page(page)) if (f2fs_is_compressed_page(page))
f2fs_end_read_compressed_page(page, PageError(page), f2fs_end_read_compressed_page(page, PageError(page),
blkaddr); blkaddr, in_task);
else else
all_compressed = false; all_compressed = false;
@@ -263,15 +265,16 @@ static void f2fs_post_read_work(struct work_struct *work)
fscrypt_decrypt_bio(ctx->bio); fscrypt_decrypt_bio(ctx->bio);
if (ctx->enabled_steps & STEP_DECOMPRESS) 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) static void f2fs_read_end_io(struct bio *bio)
{ {
struct f2fs_sb_info *sbi = F2FS_P_SB(bio_first_page_all(bio)); struct f2fs_sb_info *sbi = F2FS_P_SB(bio_first_page_all(bio));
struct bio_post_read_ctx *ctx; struct bio_post_read_ctx *ctx;
bool intask = in_task();
iostat_update_and_unbind_ctx(bio, 0); iostat_update_and_unbind_ctx(bio, 0);
ctx = bio->bi_private; ctx = bio->bi_private;
@@ -282,16 +285,29 @@ static void f2fs_read_end_io(struct bio *bio)
} }
if (bio->bi_status) { if (bio->bi_status) {
f2fs_finish_read_bio(bio); f2fs_finish_read_bio(bio, intask);
return; return;
} }
if (ctx && (ctx->enabled_steps & (STEP_DECRYPT | STEP_DECOMPRESS))) { if (ctx) {
INIT_WORK(&ctx->work, f2fs_post_read_work); unsigned int enabled_steps = ctx->enabled_steps &
queue_work(ctx->sbi->post_read_wq, &ctx->work); (STEP_DECRYPT | STEP_DECOMPRESS);
} else {
f2fs_verify_and_finish_bio(bio); /*
* 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) static void f2fs_write_end_io(struct bio *bio)
@@ -1687,8 +1703,6 @@ sync_out:
*/ */
f2fs_wait_on_block_writeback_range(inode, f2fs_wait_on_block_writeback_range(inode,
map->m_pblk, map->m_len); map->m_pblk, map->m_len);
invalidate_mapping_pages(META_MAPPING(sbi),
map->m_pblk, map->m_pblk);
if (map->m_multidev_dio) { if (map->m_multidev_dio) {
block_t blk_addr = map->m_pblk; block_t blk_addr = map->m_pblk;
@@ -2240,7 +2254,7 @@ skip_reading_dnode:
if (f2fs_load_compressed_page(sbi, page, blkaddr)) { if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
if (atomic_dec_and_test(&dic->remaining_pages)) if (atomic_dec_and_test(&dic->remaining_pages))
f2fs_decompress_cluster(dic); f2fs_decompress_cluster(dic, true);
continue; continue;
} }
@@ -2258,7 +2272,7 @@ submit_and_realloc:
page->index, for_write); page->index, for_write);
if (IS_ERR(bio)) { if (IS_ERR(bio)) {
ret = PTR_ERR(bio); ret = PTR_ERR(bio);
f2fs_decompress_end_io(dic, ret); f2fs_decompress_end_io(dic, ret, true);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
*bio_ret = NULL; *bio_ret = NULL;
return ret; return ret;
@@ -2747,6 +2761,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
.submitted = false, .submitted = false,
.compr_blocks = compr_blocks, .compr_blocks = compr_blocks,
.need_lock = LOCK_RETRY, .need_lock = LOCK_RETRY,
.post_read = f2fs_post_read_required(inode),
.io_type = io_type, .io_type = io_type,
.io_wbc = wbc, .io_wbc = wbc,
.bio = bio, .bio = bio,
@@ -2918,7 +2933,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
{ {
int ret = 0; int ret = 0;
int done = 0, retry = 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 f2fs_sb_info *sbi = F2FS_M_SB(mapping);
struct bio *bio = NULL; struct bio *bio = NULL;
sector_t last_block; sector_t last_block;
@@ -2949,8 +2964,6 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
int submitted = 0; int submitted = 0;
int i; int i;
pagevec_init(&pvec);
if (get_dirty_pages(mapping->host) <= if (get_dirty_pages(mapping->host) <=
SM_I(F2FS_M_SB(mapping))->min_hot_blocks) SM_I(F2FS_M_SB(mapping))->min_hot_blocks)
set_inode_flag(mapping->host, FI_HOT_DATA); set_inode_flag(mapping->host, FI_HOT_DATA);
@@ -2976,13 +2989,13 @@ retry:
tag_pages_for_writeback(mapping, index, end); tag_pages_for_writeback(mapping, index, end);
done_index = index; done_index = index;
while (!done && !retry && (index <= end)) { while (!done && !retry && (index <= end)) {
nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index, end, nr_pages = find_get_pages_range_tag(mapping, &index, end,
tag); tag, F2FS_ONSTACK_PAGES, pages);
if (nr_pages == 0) if (nr_pages == 0)
break; break;
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i]; struct page *page = pages[i];
bool need_readd; bool need_readd;
readd: readd:
need_readd = false; need_readd = false;
@@ -3013,6 +3026,10 @@ readd:
if (!f2fs_cluster_is_empty(&cc)) if (!f2fs_cluster_is_empty(&cc))
goto lock_page; goto lock_page;
if (f2fs_all_cluster_page_ready(&cc,
pages, i, nr_pages, true))
goto lock_page;
ret2 = f2fs_prepare_compress_overwrite( ret2 = f2fs_prepare_compress_overwrite(
inode, &pagep, inode, &pagep,
page->index, &fsdata); page->index, &fsdata);
@@ -3023,8 +3040,8 @@ readd:
} else if (ret2 && } else if (ret2 &&
(!f2fs_compress_write_end(inode, (!f2fs_compress_write_end(inode,
fsdata, page->index, 1) || fsdata, page->index, 1) ||
!f2fs_all_cluster_page_loaded(&cc, !f2fs_all_cluster_page_ready(&cc,
&pvec, i, nr_pages))) { pages, i, nr_pages, false))) {
retry = 1; retry = 1;
break; break;
} }
@@ -3114,7 +3131,7 @@ next:
if (need_readd) if (need_readd)
goto readd; goto readd;
} }
pagevec_release(&pvec); release_pages(pages, nr_pages);
cond_resched(); cond_resched();
} }
#ifdef CONFIG_F2FS_FS_COMPRESSION #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; struct inode *cow_inode = F2FS_I(inode)->cow_inode;
pgoff_t index = page->index; pgoff_t index = page->index;
int err = 0; 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 is beyond the end of file, reserve a new block in COW inode */
if ((pos & PAGE_MASK) >= i_size_read(inode)) if ((pos & PAGE_MASK) >= i_size_read(inode))
return __reserve_data_block(cow_inode, index, blk_addr, goto reserve_block;
node_changed);
/* Look for the block in COW inode first */ /* Look for the block in COW inode first */
err = __find_data_block(cow_inode, index, blk_addr); 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) if (err)
return err; return err;
reserve_block:
/* Finally, we should reserve a new block in COW inode for the update */ /* Finally, we should reserve a new block in COW inode for the update */
err = __reserve_data_block(cow_inode, index, blk_addr, node_changed); err = __reserve_data_block(cow_inode, index, blk_addr, node_changed);
if (err) if (err)
return err; return err;
inc_atomic_write_cnt(inode);
if (ori_blk_addr != NULL_ADDR) if (ori_blk_addr != NULL_ADDR)
*blk_addr = ori_blk_addr; *blk_addr = ori_blk_addr;

View File

@@ -39,7 +39,7 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi)
bimodal = 0; bimodal = 0;
total_vblocks = 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; hblks_per_sec = blks_per_sec / 2;
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
vblocks = get_valid_blocks(sbi, segno, true); vblocks = get_valid_blocks(sbi, segno, true);

View File

@@ -156,6 +156,7 @@ struct f2fs_mount_info {
int fsync_mode; /* fsync policy */ int fsync_mode; /* fsync policy */
int fs_mode; /* fs mode: LFS or ADAPTIVE */ int fs_mode; /* fs mode: LFS or ADAPTIVE */
int bggc_mode; /* bggc mode: off, on or sync */ int bggc_mode; /* bggc mode: off, on or sync */
int memory_mode; /* memory mode */
int discard_unit; /* int discard_unit; /*
* discard command's offset/size should * discard command's offset/size should
* be aligned to this unit: block, * be aligned to this unit: block,
@@ -226,7 +227,6 @@ enum {
#define CP_PAUSE 0x00000040 #define CP_PAUSE 0x00000040
#define CP_RESIZE 0x00000080 #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_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */
#define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */ #define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */
#define DEF_MID_DISCARD_ISSUE_TIME 500 /* 500 ms, if device busy */ #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_MAX_RA_BLOCKS BIO_MAX_VECS
#define RECOVERY_MIN_RA_BLOCKS 1 #define RECOVERY_MIN_RA_BLOCKS 1
#define F2FS_ONSTACK_PAGES 16 /* nr of onstack pages */
struct rb_entry { struct rb_entry {
struct rb_node rb_node; /* rb node located in rb-tree */ struct rb_node rb_node; /* rb node located in rb-tree */
union { union {
@@ -754,6 +756,7 @@ enum {
FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */ FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */
FI_COMPRESS_RELEASED, /* compressed blocks were released */ FI_COMPRESS_RELEASED, /* compressed blocks were released */
FI_ALIGNED_WRITE, /* enable aligned write */ FI_ALIGNED_WRITE, /* enable aligned write */
FI_COW_FILE, /* indicate COW file */
FI_MAX, /* max flag, never be used */ 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 char i_compress_level; /* compress level (lz4hc,zstd) */
unsigned short i_compress_flag; /* compress flag */ unsigned short i_compress_flag; /* compress flag */
unsigned int i_cluster_size; /* cluster size */ unsigned int i_cluster_size; /* cluster size */
unsigned int atomic_write_cnt;
}; };
static inline void get_extent_info(struct extent_info *ext, 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 */ bool retry; /* need to reallocate block address */
int compr_blocks; /* # of compressed block addresses */ int compr_blocks; /* # of compressed block addresses */
bool encrypted; /* indicate file is encrypted */ bool encrypted; /* indicate file is encrypted */
bool post_read; /* require post read */
enum iostat_type io_type; /* io type */ enum iostat_type io_type; /* io type */
struct writeback_control *io_wbc; /* writeback control */ struct writeback_control *io_wbc; /* writeback control */
struct bio **bio; /* bio for ipu */ struct bio **bio; /* bio for ipu */
@@ -1231,7 +1237,6 @@ struct f2fs_dev_info {
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
unsigned int nr_blkz; /* Total number of zones */ unsigned int nr_blkz; /* Total number of zones */
unsigned long *blkz_seq; /* Bitmap indicating sequential zones */ unsigned long *blkz_seq; /* Bitmap indicating sequential zones */
block_t *zone_capacity_blocks; /* Array of zone capacity in blks */
#endif #endif
}; };
@@ -1357,6 +1362,13 @@ enum {
DISCARD_UNIT_SECTION, /* basic discard unit is section */ 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 int f2fs_test_bit(unsigned int nr, char *addr);
static inline void f2fs_set_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); 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 *private; /* payload buffer for specified decompression algorithm */
void *private2; /* extra payload buffer */ void *private2; /* extra payload buffer */
struct work_struct verity_work; /* work to verify the decompressed pages */ 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)) #define NULL_CLUSTER ((unsigned int)(~0))
@@ -1661,6 +1674,7 @@ struct f2fs_sb_info {
unsigned int meta_ino_num; /* meta inode number*/ unsigned int meta_ino_num; /* meta inode number*/
unsigned int log_blocks_per_seg; /* log2 blocks per segment */ unsigned int log_blocks_per_seg; /* log2 blocks per segment */
unsigned int blocks_per_seg; /* 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 segs_per_sec; /* segments per section */
unsigned int secs_per_zone; /* sections per zone */ unsigned int secs_per_zone; /* sections per zone */
unsigned int total_sections; /* total section count */ 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_chunk; /* max chunk size for block fragmentation mode */
int max_fragment_hole; /* max hole 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 #ifdef CONFIG_F2FS_FS_COMPRESSION
struct kmem_cache *page_array_slab; /* page array entry */ struct kmem_cache *page_array_slab; /* page array entry */
unsigned int page_array_slab_size; /* default page array slab size */ 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); 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) static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type)
{ {
return atomic_read(&sbi->nr_pages[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); 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) static inline void f2fs_put_page(struct page *page, int unlock)
{ {
if (!page) 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); 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) static inline bool f2fs_is_first_block_written(struct inode *inode)
{ {
return is_inode_flag_set(inode, FI_FIRST_BLOCK_WRITTEN); 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); bool f2fs_is_compress_backend_ready(struct inode *inode);
int f2fs_init_compress_mempool(void); int f2fs_init_compress_mempool(void);
void f2fs_destroy_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, 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_is_empty(struct compress_ctx *cc);
bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index); 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, bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
int index, int nr_pages); int index, int nr_pages, bool uptodate);
bool f2fs_sanity_check_cluster(struct dnode_of_data *dn); bool f2fs_sanity_check_cluster(struct dnode_of_data *dn);
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page); void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page);
int f2fs_write_multi_pages(struct compress_ctx *cc, 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, unsigned nr_pages, sector_t *last_block_in_bio,
bool is_readahead, bool for_write); bool is_readahead, bool for_write);
struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc); 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_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
void f2fs_put_page_dic(struct page *page); 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); unsigned int f2fs_cluster_blocks_are_contiguous(struct dnode_of_data *dn);
int f2fs_init_compress_ctx(struct compress_ctx *cc); int f2fs_init_compress_ctx(struct compress_ctx *cc);
void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse); 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 int f2fs_init_compress_mempool(void) { return 0; }
static inline void f2fs_destroy_compress_mempool(void) { } 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, 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); 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); WARN_ON_ONCE(1);
} }
@@ -4250,8 +4289,9 @@ static inline void f2fs_update_extent_tree_range_compressed(struct inode *inode,
unsigned int c_len) { } unsigned int c_len) { }
#endif #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); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
F2FS_I(inode)->i_compress_algorithm = F2FS_I(inode)->i_compress_algorithm =
@@ -4274,6 +4314,10 @@ static inline void set_compress_context(struct inode *inode)
stat_inc_compr_inode(inode); stat_inc_compr_inode(inode);
inc_compr_inode_stat(inode); inc_compr_inode_stat(inode);
f2fs_mark_inode_dirty_sync(inode, true); f2fs_mark_inode_dirty_sync(inode, true);
return 0;
#else
return -EOPNOTSUPP;
#endif
} }
static inline bool f2fs_disable_compressed_file(struct inode *inode) 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; 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) static inline bool f2fs_may_compress(struct inode *inode)
{ {
if (IS_SWAPFILE(inode) || f2fs_is_pinned_file(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 false;
return S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode); 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 */ /* disallow direct IO if any of devices has unaligned blksize */
if (f2fs_is_multi_device(sbi) && !sbi->aligned_blksize) if (f2fs_is_multi_device(sbi) && !sbi->aligned_blksize)
return true; 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 (f2fs_lfs_mode(sbi) && (rw == WRITE)) {
if (block_unaligned_IO(inode, iocb, iter)) if (block_unaligned_IO(inode, iocb, iter))
return true; return true;

View File

@@ -1279,7 +1279,7 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode,
f2fs_put_page(psrc, 1); f2fs_put_page(psrc, 1);
return PTR_ERR(pdst); return PTR_ERR(pdst);
} }
f2fs_copy_page(psrc, pdst); memcpy_page(pdst, 0, psrc, 0, PAGE_SIZE);
set_page_dirty(pdst); set_page_dirty(pdst);
f2fs_put_page(pdst, 1); f2fs_put_page(pdst, 1);
f2fs_put_page(psrc, 1); f2fs_put_page(psrc, 1);
@@ -1682,7 +1682,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
return 0; return 0;
if (f2fs_is_pinned_file(inode)) { 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); block_t sec_len = roundup(map.m_len, sec_blks);
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) atomic_read(&inode->i_writecount) != 1)
return 0; return 0;
if (f2fs_is_atomic_file(inode)) f2fs_abort_atomic_write(inode, true);
f2fs_abort_atomic_write(inode, true);
return 0; 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 * until all the writers close its file. Since this should be done
* before dropping file lock, it needs to do in ->flush. * before dropping file lock, it needs to do in ->flush.
*/ */
if (f2fs_is_atomic_file(inode) && if (F2FS_I(inode)->atomic_write_task == current)
F2FS_I(inode)->atomic_write_task == current)
f2fs_abort_atomic_write(inode, true); f2fs_abort_atomic_write(inode, true);
return 0; 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 (masked_flags & F2FS_COMPR_FL) {
if (!f2fs_disable_compressed_file(inode)) if (!f2fs_disable_compressed_file(inode))
return -EINVAL; return -EINVAL;
} } else {
if (iflags & F2FS_NOCOMP_FL)
return -EINVAL;
if (iflags & F2FS_COMPR_FL) {
if (!f2fs_may_compress(inode)) if (!f2fs_may_compress(inode))
return -EINVAL; return -EINVAL;
if (S_ISREG(inode->i_mode) && inode->i_size) if (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))
return -EINVAL; return -EINVAL;
if (set_compress_context(inode))
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); fi->i_flags = iflags | (fi->i_flags & ~mask);
f2fs_bug_on(F2FS_I_SB(inode), (fi->i_flags & F2FS_COMPR_FL) && 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]); spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
set_inode_flag(inode, FI_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); clear_inode_flag(fi->cow_inode, FI_INLINE_DATA);
f2fs_up_write(&fi->i_gc_rwsem[WRITE]); f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
f2fs_update_time(sbi, REQ_TIME); f2fs_update_time(sbi, REQ_TIME);
fi->atomic_write_task = current; fi->atomic_write_task = current;
stat_update_max_atomic_write(inode); stat_update_max_atomic_write(inode);
fi->atomic_write_cnt = 0;
out: out:
inode_unlock(inode); inode_unlock(inode);
mnt_drop_write_file(filp); mnt_drop_write_file(filp);
@@ -2116,6 +2108,30 @@ unlock_out:
return ret; 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) static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
@@ -2434,7 +2450,7 @@ do_more:
ret = -EAGAIN; ret = -EAGAIN;
goto out; goto out;
} }
range->start += BLKS_PER_SEC(sbi); range->start += CAP_BLKS_PER_SEC(sbi);
if (range->start <= end) if (range->start <= end)
goto do_more; goto do_more;
out: out:
@@ -2559,7 +2575,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
goto out; 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 * 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++) { for (i = 0; i < page_len; i++, redirty_idx++) {
page = find_lock_page(mapping, redirty_idx); page = find_lock_page(mapping, redirty_idx);
if (!page) {
ret = -ENOMEM; /* It will never fail, when page has pinned above */
break; f2fs_bug_on(F2FS_I_SB(inode), !page);
}
set_page_dirty(page); set_page_dirty(page);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
@@ -3947,6 +3963,11 @@ static int f2fs_ioc_decompress_file(struct file *filp, unsigned long arg)
goto out; 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); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret) if (ret)
goto out; goto out;
@@ -4014,6 +4035,11 @@ static int f2fs_ioc_compress_file(struct file *filp, unsigned long arg)
goto out; 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); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret) if (ret)
goto out; 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); return f2fs_ioc_start_atomic_write(filp);
case F2FS_IOC_COMMIT_ATOMIC_WRITE: case F2FS_IOC_COMMIT_ATOMIC_WRITE:
return f2fs_ioc_commit_atomic_write(filp); 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_START_VOLATILE_WRITE:
case F2FS_IOC_RELEASE_VOLATILE_WRITE: case F2FS_IOC_RELEASE_VOLATILE_WRITE:
case F2FS_IOC_ABORT_VOLATILE_WRITE:
return -EOPNOTSUPP; return -EOPNOTSUPP;
case F2FS_IOC_SHUTDOWN: case F2FS_IOC_SHUTDOWN:
return f2fs_ioc_shutdown(filp, arg); 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_COMMIT_ATOMIC_WRITE:
case F2FS_IOC_START_VOLATILE_WRITE: case F2FS_IOC_START_VOLATILE_WRITE:
case F2FS_IOC_RELEASE_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 F2FS_IOC_SHUTDOWN:
case FITRIM: case FITRIM:
case FS_IOC_SET_ENCRYPTION_POLICY: case FS_IOC_SET_ENCRYPTION_POLICY:

View File

@@ -150,8 +150,11 @@ do_gc:
gc_control.nr_free_secs = foreground ? 1 : 0; gc_control.nr_free_secs = foreground ? 1 : 0;
/* if return value is not zero, no victim was selected */ /* if return value is not zero, no victim was selected */
if (f2fs_gc(sbi, &gc_control)) if (f2fs_gc(sbi, &gc_control)) {
wait_ms = gc_th->no_gc_sleep_time; /* don't bother wait_ms by foreground gc */
if (!foreground)
wait_ms = gc_th->no_gc_sleep_time;
}
if (foreground) if (foreground)
wake_up_all(&gc_th->fggc_wq); 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 age, u, accu;
unsigned long long max_mtime = sit_i->dirty_max_mtime; unsigned long long max_mtime = sit_i->dirty_max_mtime;
unsigned long long min_mtime = sit_i->dirty_min_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 vblocks;
unsigned int dirty_threshold = max(am->max_candidate_count, unsigned int dirty_threshold = max(am->max_candidate_count,
am->candidate_ratio * am->candidate_ratio *
@@ -1488,7 +1491,7 @@ next_step:
*/ */
if ((gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) || if ((gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) ||
(!force_migrate && get_valid_blocks(sbi, segno, true) == (!force_migrate && get_valid_blocks(sbi, segno, true) ==
BLKS_PER_SEC(sbi))) CAP_BLKS_PER_SEC(sbi)))
return submitted; return submitted;
if (check_valid_map(sbi, segno, off) == 0) if (check_valid_map(sbi, segno, off) == 0)

View File

@@ -120,15 +120,13 @@ static inline block_t free_user_blocks(struct f2fs_sb_info *sbi)
return free_blks - ovp_blks; 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; 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) static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
{ {
block_t invalid_user_blocks = sbi->user_block_count - block_t user_block_count = sbi->user_block_count;
written_block_count(sbi); block_t invalid_user_blocks = user_block_count -
written_block_count(sbi);
/* /*
* Background GC is triggered with the following conditions. * Background GC is triggered with the following conditions.
* 1. There are a number of invalid blocks. * 1. There are a number of invalid blocks.
* 2. There is not enough free space. * 2. There is not enough free space.
*/ */
if (invalid_user_blocks > limit_invalid_user_blocks(sbi) && return (invalid_user_blocks >
free_user_blocks(sbi) < limit_free_user_blocks(sbi)) limit_invalid_user_blocks(user_block_count) &&
return true; free_user_blocks(sbi) <
return false; limit_free_user_blocks(invalid_user_blocks));
} }

View File

@@ -744,8 +744,7 @@ void f2fs_evict_inode(struct inode *inode)
nid_t xnid = F2FS_I(inode)->i_xattr_nid; nid_t xnid = F2FS_I(inode)->i_xattr_nid;
int err = 0; 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); trace_f2fs_evict_inode(inode);
truncate_inode_pages_final(&inode->i_data); truncate_inode_pages_final(&inode->i_data);

View File

@@ -91,8 +91,9 @@ static inline void __record_iostat_latency(struct f2fs_sb_info *sbi)
unsigned int cnt; unsigned int cnt;
struct f2fs_iostat_latency iostat_lat[MAX_IO_TYPE][NR_PAGE_TYPE]; struct f2fs_iostat_latency iostat_lat[MAX_IO_TYPE][NR_PAGE_TYPE];
struct iostat_lat_info *io_lat = sbi->iostat_io_lat; 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 (idx = 0; idx < MAX_IO_TYPE; idx++) {
for (io = 0; io < NR_PAGE_TYPE; io++) { for (io = 0; io < NR_PAGE_TYPE; io++) {
cnt = io_lat->bio_cnt[idx][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; 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); 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]; unsigned long long iostat_diff[NR_IO_TYPE];
int i; int i;
unsigned long flags;
if (time_is_after_jiffies(sbi->iostat_next_period)) if (time_is_after_jiffies(sbi->iostat_next_period))
return; return;
/* Need double check under the lock */ /* 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)) { if (time_is_after_jiffies(sbi->iostat_next_period)) {
spin_unlock_bh(&sbi->iostat_lock); spin_unlock_irqrestore(&sbi->iostat_lock, flags);
return; return;
} }
sbi->iostat_next_period = jiffies + 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->prev_rw_iostat[i] = sbi->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); 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; struct iostat_lat_info *io_lat = sbi->iostat_io_lat;
int i; int i;
spin_lock_bh(&sbi->iostat_lock); spin_lock_irq(&sbi->iostat_lock);
for (i = 0; i < NR_IO_TYPE; i++) { for (i = 0; i < NR_IO_TYPE; i++) {
sbi->rw_iostat[i] = 0; sbi->rw_iostat[i] = 0;
sbi->prev_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)); 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, void f2fs_update_iostat(struct f2fs_sb_info *sbi,
enum iostat_type type, unsigned long long io_bytes) enum iostat_type type, unsigned long long io_bytes)
{ {
unsigned long flags;
if (!sbi->iostat_enable) if (!sbi->iostat_enable)
return; return;
spin_lock_bh(&sbi->iostat_lock); spin_lock_irqsave(&sbi->iostat_lock, flags);
sbi->rw_iostat[type] += io_bytes; sbi->rw_iostat[type] += io_bytes;
if (type == APP_BUFFERED_IO || type == APP_DIRECT_IO) 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) if (type == APP_BUFFERED_READ_IO || type == APP_DIRECT_READ_IO)
sbi->rw_iostat[APP_READ_IO] += io_bytes; 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); 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 f2fs_sb_info *sbi = iostat_ctx->sbi;
struct iostat_lat_info *io_lat = sbi->iostat_io_lat; struct iostat_lat_info *io_lat = sbi->iostat_io_lat;
int idx; int idx;
unsigned long flags;
if (!sbi->iostat_enable) if (!sbi->iostat_enable)
return; return;
@@ -202,12 +207,12 @@ static inline void __update_iostat_latency(struct bio_iostat_ctx *iostat_ctx,
idx = WRITE_ASYNC_IO; 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->sum_lat[idx][iotype] += ts_diff;
io_lat->bio_cnt[idx][iotype]++; io_lat->bio_cnt[idx][iotype]++;
if (ts_diff > io_lat->peak_lat[idx][iotype]) if (ts_diff > io_lat->peak_lat[idx][iotype])
io_lat->peak_lat[idx][iotype] = ts_diff; 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) void iostat_update_and_unbind_ctx(struct bio *bio, int rw)

View File

@@ -328,6 +328,7 @@ static void set_compress_inode(struct f2fs_sb_info *sbi, struct inode *inode,
continue; continue;
/* Do not use inline_data with compression */ /* Do not use inline_data with compression */
stat_dec_inline_inode(inode);
clear_inode_flag(inode, FI_INLINE_DATA); clear_inode_flag(inode, FI_INLINE_DATA);
set_compress_context(inode); set_compress_context(inode);
return; return;

View File

@@ -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); dec_valid_node_count(sbi, dn->inode, !ofs);
goto fail; 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 #endif
new_ni.nid = dn->nid; new_ni.nid = dn->nid;
new_ni.ino = dn->inode->i_ino; new_ni.ino = dn->inode->i_ino;
@@ -1450,7 +1454,9 @@ page_hit:
out_err: out_err:
ClearPageUptodate(page); ClearPageUptodate(page);
out_put_err: 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); f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
} }
@@ -1943,7 +1949,6 @@ next_step:
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i]; struct page *page = pvec.pages[i];
bool submitted = false; bool submitted = false;
bool may_dirty = true;
/* give a priority to WB_SYNC threads */ /* give a priority to WB_SYNC threads */
if (atomic_read(&sbi->wb_sync_req[NODE]) && if (atomic_read(&sbi->wb_sync_req[NODE]) &&
@@ -1996,11 +2001,8 @@ continue_unlock:
} }
/* flush dirty inode */ /* flush dirty inode */
if (IS_INODE(page) && may_dirty) { if (IS_INODE(page) && flush_dirty_inode(page))
may_dirty = false; goto lock_node;
if (flush_dirty_inode(page))
goto lock_node;
}
write_node: write_node:
f2fs_wait_on_page_writeback(page, NODE, true, true); f2fs_wait_on_page_writeback(page, NODE, true, true);

View File

@@ -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_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
if (f2fs_is_atomic_file(inode)) { if (!f2fs_is_atomic_file(inode))
if (clean) return;
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);
spin_lock(&sbi->inode_lock[ATOMIC_FILE]); if (clean)
sbi->atomic_files--; truncate_inode_pages_final(inode->i_mapping);
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 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, static int __replace_atomic_write_block(struct inode *inode, pgoff_t index,
@@ -334,6 +336,11 @@ next:
} }
out: 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); __complete_revoke_list(inode, &revoke_list, ret ? true : false);
return ret; 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); get_valid_blocks(sbi, segno, true);
f2fs_bug_on(sbi, unlikely(!valid_blocks || 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)) if (!IS_CURSEC(sbi, secno))
set_bit(secno, dirty_i->dirty_secmap); 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); unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
if (!valid_blocks || if (!valid_blocks ||
valid_blocks == BLKS_PER_SEC(sbi)) { valid_blocks == CAP_BLKS_PER_SEC(sbi)) {
clear_bit(secno, dirty_i->dirty_secmap); clear_bit(secno, dirty_i->dirty_secmap);
return; return;
} }
@@ -3167,7 +3174,7 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
return CURSEG_COLD_DATA; return CURSEG_COLD_DATA;
if (file_is_hot(inode) || if (file_is_hot(inode) ||
is_inode_flag_set(inode, FI_HOT_DATA) || is_inode_flag_set(inode, FI_HOT_DATA) ||
f2fs_is_atomic_file(inode)) f2fs_is_cow_file(inode))
return CURSEG_HOT_DATA; return CURSEG_HOT_DATA;
return f2fs_rw_hint_to_seg_type(inode->i_write_hint); return f2fs_rw_hint_to_seg_type(inode->i_write_hint);
} else { } else {
@@ -3434,7 +3441,8 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
goto drop_bio; 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); fio->new_blkaddr, fio->new_blkaddr);
stat_inc_inplace_blocks(fio->sbi); 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, void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr,
block_t len) block_t len)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
block_t i; block_t i;
if (!f2fs_post_read_required(inode))
return;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
f2fs_wait_on_block_writeback(inode, blkaddr + 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) 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; return err;
seg_info_from_raw_sit(se, &sit); 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; sit_valid_blocks[SE_PAGETYPE(se)] += se->valid_blocks;
if (f2fs_block_unit_discard(sbi)) { if (f2fs_block_unit_discard(sbi)) {
@@ -4411,6 +4431,13 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
break; break;
seg_info_from_raw_sit(se, &sit); 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; sit_valid_blocks[SE_PAGETYPE(se)] += se->valid_blocks;
if (f2fs_block_unit_discard(sbi)) { 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); struct free_segmap_info *free_i = FREE_I(sbi);
unsigned int segno = 0, offset = 0, secno; unsigned int segno = 0, offset = 0, secno;
block_t valid_blocks, usable_blks_in_seg; block_t valid_blocks, usable_blks_in_seg;
block_t blks_per_sec = BLKS_PER_SEC(sbi);
while (1) { while (1) {
/* find dirty segment based on free segmap */ /* 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); valid_blocks = get_valid_blocks(sbi, segno, true);
secno = GET_SEC_FROM_SEG(sbi, segno); 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; continue;
if (IS_CURSEC(sbi, secno)) if (IS_CURSEC(sbi, secno))
continue; 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( static inline unsigned int f2fs_usable_zone_segs_in_sec(
struct f2fs_sb_info *sbi, unsigned int segno) 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)); 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); 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)) if (is_conv_zone(sbi, zone_idx, dev_idx))
return sbi->segs_per_sec; return sbi->segs_per_sec;
/* if (!sbi->unusable_blocks_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)
return sbi->segs_per_sec; return sbi->segs_per_sec;
/* Get the segment count beyond zone capacity block */ /* Get the segment count beyond zone capacity block */
unusable_segs_in_sec = (sbi->blocks_per_blkz - return sbi->segs_per_sec - (sbi->unusable_blocks_per_sec >>
FDEV(dev_idx).zone_capacity_blocks[zone_idx]) >> sbi->log_blocks_per_seg);
sbi->log_blocks_per_seg;
return sbi->segs_per_sec - unusable_segs_in_sec;
} }
/* /*
@@ -4945,12 +4965,11 @@ static inline unsigned int f2fs_usable_zone_blks_in_seg(
if (is_conv_zone(sbi, zone_idx, dev_idx)) if (is_conv_zone(sbi, zone_idx, dev_idx))
return sbi->blocks_per_seg; return sbi->blocks_per_seg;
if (!FDEV(dev_idx).zone_capacity_blocks) if (!sbi->unusable_blocks_per_sec)
return sbi->blocks_per_seg; return sbi->blocks_per_seg;
sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno)); sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno));
sec_cap_blkaddr = sec_start_blkaddr + sec_cap_blkaddr = sec_start_blkaddr + CAP_BLKS_PER_SEC(sbi);
FDEV(dev_idx).zone_capacity_blocks[zone_idx];
/* /*
* If segment starts before zone capacity and spans beyond * If segment starts before zone capacity and spans beyond

View File

@@ -101,6 +101,9 @@ static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi,
GET_SEGNO_FROM_SEG0(sbi, blk_addr))) GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
#define BLKS_PER_SEC(sbi) \ #define BLKS_PER_SEC(sbi) \
((sbi)->segs_per_sec * (sbi)->blocks_per_seg) ((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) \ #define GET_SEC_FROM_SEG(sbi, segno) \
(((segno) == -1) ? -1: (segno) / (sbi)->segs_per_sec) (((segno) == -1) ? -1: (segno) / (sbi)->segs_per_sec)
#define GET_SEG_FROM_SEC(sbi, secno) \ #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_DENTS) +
get_pages(sbi, F2FS_DIRTY_IMETA); get_pages(sbi, F2FS_DIRTY_IMETA);
unsigned int total_dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS); unsigned int total_dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS);
unsigned int node_secs = total_node_blocks / BLKS_PER_SEC(sbi); unsigned int node_secs = total_node_blocks / CAP_BLKS_PER_SEC(sbi);
unsigned int dent_secs = total_dent_blocks / BLKS_PER_SEC(sbi); unsigned int dent_secs = total_dent_blocks / CAP_BLKS_PER_SEC(sbi);
unsigned int node_blocks = total_node_blocks % BLKS_PER_SEC(sbi); unsigned int node_blocks = total_node_blocks % CAP_BLKS_PER_SEC(sbi);
unsigned int dent_blocks = total_dent_blocks % BLKS_PER_SEC(sbi); unsigned int dent_blocks = total_dent_blocks % CAP_BLKS_PER_SEC(sbi);
unsigned int free, need_lower, need_upper; unsigned int free, need_lower, need_upper;
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))

View File

@@ -8,6 +8,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
@@ -159,6 +160,7 @@ enum {
Opt_gc_merge, Opt_gc_merge,
Opt_nogc_merge, Opt_nogc_merge,
Opt_discard_unit, Opt_discard_unit,
Opt_memory_mode,
Opt_err, Opt_err,
}; };
@@ -235,6 +237,7 @@ static match_table_t f2fs_tokens = {
{Opt_gc_merge, "gc_merge"}, {Opt_gc_merge, "gc_merge"},
{Opt_nogc_merge, "nogc_merge"}, {Opt_nogc_merge, "nogc_merge"},
{Opt_discard_unit, "discard_unit=%s"}, {Opt_discard_unit, "discard_unit=%s"},
{Opt_memory_mode, "memory=%s"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
@@ -499,9 +502,19 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
bool is_remount) bool is_remount)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); 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; 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)) { if (!f2fs_sb_has_encrypt(sbi)) {
f2fs_err(sbi, "Encrypt feature is off"); f2fs_err(sbi, "Encrypt feature is off");
return -EINVAL; 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 * 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. * 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"); f2fs_warn(sbi, "Can't set test_dummy_encryption on remount");
return -EINVAL; return -EINVAL;
} }
err = fscrypt_set_test_dummy_encryption(
sb, arg->from, &F2FS_OPTION(sbi).dummy_enc_policy); err = fscrypt_parse_test_dummy_encryption(&param, policy);
if (err) { if (err) {
if (err == -EEXIST) if (err == -EEXIST)
f2fs_warn(sbi, f2fs_warn(sbi,
@@ -531,12 +544,14 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
opt, err); opt, err);
return -EINVAL; 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"); f2fs_warn(sbi, "Test dummy encryption mode enabled");
return 0; return 0;
#else
f2fs_warn(sbi, "test_dummy_encryption option not supported");
return -EINVAL;
#endif
} }
#ifdef CONFIG_F2FS_FS_COMPRESSION #ifdef CONFIG_F2FS_FS_COMPRESSION
@@ -1229,6 +1244,22 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
} }
kfree(name); kfree(name);
break; 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: default:
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value", f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
p); p);
@@ -1383,8 +1414,7 @@ static int f2fs_drop_inode(struct inode *inode)
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
spin_unlock(&inode->i_lock); 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 */ /* should remain fi->extent_tree for writepage */
f2fs_destroy_extent_node(inode); 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); blkdev_put(FDEV(i).bdev, FMODE_EXCL);
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
kvfree(FDEV(i).blkz_seq); kvfree(FDEV(i).blkz_seq);
kfree(FDEV(i).zone_capacity_blocks);
#endif #endif
} }
kvfree(sbi->devs); 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) else if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SECTION)
seq_printf(seq, ",discard_unit=%s", "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; 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_ext_cnt = 0;
F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS; F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON; F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL;
sbi->sb->s_flags &= ~SB_INLINECRYPT; 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_chunk = DEF_FRAGMENT_SIZE;
sbi->max_fragment_hole = DEF_FRAGMENT_SIZE; sbi->max_fragment_hole = DEF_FRAGMENT_SIZE;
spin_lock_init(&sbi->gc_urgent_high_lock); spin_lock_init(&sbi->gc_urgent_high_lock);
atomic64_set(&sbi->current_atomic_write, 0);
sbi->dir_level = DEF_DIR_LEVEL; sbi->dir_level = DEF_DIR_LEVEL;
sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL; sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
@@ -3640,24 +3676,29 @@ err_valid_block:
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
struct f2fs_report_zones_args { struct f2fs_report_zones_args {
struct f2fs_sb_info *sbi;
struct f2fs_dev_info *dev; struct f2fs_dev_info *dev;
bool zone_cap_mismatch;
}; };
static int f2fs_report_zone_cb(struct blk_zone *zone, unsigned int idx, static int f2fs_report_zone_cb(struct blk_zone *zone, unsigned int idx,
void *data) void *data)
{ {
struct f2fs_report_zones_args *rz_args = 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) if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
return 0; return 0;
set_bit(idx, rz_args->dev->blkz_seq); set_bit(idx, rz_args->dev->blkz_seq);
rz_args->dev->zone_capacity_blocks[idx] = zone->capacity >> if (!rz_args->sbi->unusable_blocks_per_sec) {
F2FS_LOG_SECTORS_PER_BLOCK; rz_args->sbi->unusable_blocks_per_sec = unusable_blocks;
if (zone->len != zone->capacity && !rz_args->zone_cap_mismatch) return 0;
rz_args->zone_cap_mismatch = true; }
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; return 0;
} }
@@ -3698,26 +3739,13 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
if (!FDEV(devi).blkz_seq) if (!FDEV(devi).blkz_seq)
return -ENOMEM; return -ENOMEM;
/* Get block zones type and zone-capacity */ rep_zone_arg.sbi = sbi;
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.dev = &FDEV(devi); 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, ret = blkdev_report_zones(bdev, 0, BLK_ALL_ZONES, f2fs_report_zone_cb,
&rep_zone_arg); &rep_zone_arg);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (!rep_zone_arg.zone_cap_mismatch) {
kfree(FDEV(devi).zone_capacity_blocks);
FDEV(devi).zone_capacity_blocks = NULL;
}
return 0; return 0;
} }
#endif #endif

View File

@@ -340,6 +340,21 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
sbi->gc_reclaimed_segs[sbi->gc_segment_mode]); 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); ui = (unsigned int *)(ptr + a->offset);
return sprintf(buf, "%u\n", *ui); return sprintf(buf, "%u\n", *ui);
@@ -609,6 +624,27 @@ out:
return count; 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; *ui = (unsigned int)t;
return count; return count;
@@ -714,6 +750,11 @@ static struct f2fs_attr f2fs_attr_##_name = { \
.offset = _offset \ .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) \ #define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \
F2FS_ATTR_OFFSET(struct_type, name, 0644, \ F2FS_ATTR_OFFSET(struct_type, name, 0644, \
f2fs_sbi_show, f2fs_sbi_store, \ f2fs_sbi_show, f2fs_sbi_store, \
@@ -812,6 +853,8 @@ F2FS_FEATURE_RO_ATTR(encrypted_casefold);
#endif /* CONFIG_FS_ENCRYPTION */ #endif /* CONFIG_FS_ENCRYPTION */
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
F2FS_FEATURE_RO_ATTR(block_zoned); F2FS_FEATURE_RO_ATTR(block_zoned);
F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, unusable_blocks_per_sec,
unusable_blocks_per_sec);
#endif #endif
F2FS_FEATURE_RO_ATTR(atomic_write); F2FS_FEATURE_RO_ATTR(atomic_write);
F2FS_FEATURE_RO_ATTR(extra_attr); 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_chunk, max_fragment_chunk);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_hole, max_fragment_hole); 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) #define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = { static struct attribute *f2fs_attrs[] = {
ATTR_LIST(gc_urgent_sleep_time), ATTR_LIST(gc_urgent_sleep_time),
@@ -920,6 +969,9 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(moved_blocks_background), ATTR_LIST(moved_blocks_background),
ATTR_LIST(avg_vblocks), ATTR_LIST(avg_vblocks),
#endif #endif
#ifdef CONFIG_BLK_DEV_ZONED
ATTR_LIST(unusable_blocks_per_sec),
#endif
#ifdef CONFIG_F2FS_FS_COMPRESSION #ifdef CONFIG_F2FS_FS_COMPRESSION
ATTR_LIST(compr_written_block), ATTR_LIST(compr_written_block),
ATTR_LIST(compr_saved_block), ATTR_LIST(compr_saved_block),
@@ -935,6 +987,10 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(gc_reclaimed_segments), ATTR_LIST(gc_reclaimed_segments),
ATTR_LIST(max_fragment_chunk), ATTR_LIST(max_fragment_chunk),
ATTR_LIST(max_fragment_hole), 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, NULL,
}; };
ATTRIBUTE_GROUPS(f2fs); ATTRIBUTE_GROUPS(f2fs);

View File

@@ -130,7 +130,7 @@
#define WORST_COMPR_FACTOR 2 #define WORST_COMPR_FACTOR 2
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
#define UBIFS_CIPHER_BLOCK_SIZE FS_CRYPTO_BLOCK_SIZE #define UBIFS_CIPHER_BLOCK_SIZE FSCRYPT_CONTENTS_ALIGNMENT
#else #else
#define UBIFS_CIPHER_BLOCK_SIZE 0 #define UBIFS_CIPHER_BLOCK_SIZE 0
#endif #endif

View File

@@ -18,10 +18,21 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <uapi/linux/fscrypt.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; union fscrypt_policy;
struct fscrypt_info; struct fscrypt_info;
struct fs_parameter;
struct seq_file; struct seq_file;
struct fscrypt_str { struct fscrypt_str {
@@ -286,10 +297,19 @@ struct fscrypt_dummy_policy {
const union fscrypt_policy *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, int fscrypt_set_test_dummy_encryption(struct super_block *sb, const char *arg,
struct fscrypt_dummy_policy *dummy_policy); struct fscrypt_dummy_policy *dummy_policy);
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep, void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
struct super_block *sb); 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 static inline void
fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy) 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 */ /* keyring.c */
void fscrypt_sb_free(struct super_block *sb); void fscrypt_sb_free(struct super_block *sb);
int fscrypt_ioctl_add_key(struct file *filp, void __user *arg); 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(struct file *filp, void __user *arg);
int fscrypt_ioctl_remove_key_all_users(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); 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 { 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, static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
char sep, char sep,
struct super_block *sb) struct super_block *sb)
{ {
} }
static inline bool
fscrypt_is_dummy_policy_set(const struct fscrypt_dummy_policy *dummy_policy)
{
return false;
}
static inline void static inline void
fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy) 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; 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) static inline int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;

View File

@@ -13,7 +13,7 @@
#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) #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_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
#define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4) #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_GARBAGE_COLLECT _IOW(F2FS_IOCTL_MAGIC, 6, __u32)
#define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) #define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7)
#define F2FS_IOC_DEFRAGMENT _IOWR(F2FS_IOCTL_MAGIC, 8, \ #define F2FS_IOC_DEFRAGMENT _IOWR(F2FS_IOCTL_MAGIC, 8, \