f2fs: catch up to v4.14-rc1
Cherry-picked from upstream-f2fs-stable-linux-4.9.y
Changes include:
commit 30da3a4de96733 ("f2fs: hurry up to issue discard after io interruption")
commit d1c363b48398d4 ("f2fs: fix to show correct discard_granularity in sysfs")
...
commit e6b120d4d01ab0 ("f2fs/fscrypt: catch up to v4.12")
commit 4d7931d72758db ("KEYS: Differentiate uses of rcu_dereference_key() and user_key_payload()")
Signed-off-by: Hyojun Kim <hyojun@google.com>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
05c85a6ddf
commit
63da4200cb
@@ -57,6 +57,15 @@ Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
|
||||
Description:
|
||||
Controls the issue rate of small discard commands.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/discard_granularity
|
||||
Date: July 2017
|
||||
Contact: "Chao Yu" <yuchao0@huawei.com>
|
||||
Description:
|
||||
Controls discard granularity of inner discard thread, inner thread
|
||||
will not issue discards with size that is smaller than granularity.
|
||||
The unit size is one block, now only support configuring in range
|
||||
of [1, 512].
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/max_victim_search
|
||||
Date: January 2014
|
||||
Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
|
||||
@@ -112,3 +121,33 @@ Date: January 2016
|
||||
Contact: "Shuoran Liu" <liushuoran@huawei.com>
|
||||
Description:
|
||||
Shows total written kbytes issued to disk.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/inject_rate
|
||||
Date: May 2016
|
||||
Contact: "Sheng Yong" <shengyong1@huawei.com>
|
||||
Description:
|
||||
Controls the injection rate.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/inject_type
|
||||
Date: May 2016
|
||||
Contact: "Sheng Yong" <shengyong1@huawei.com>
|
||||
Description:
|
||||
Controls the injection type.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/reserved_blocks
|
||||
Date: June 2017
|
||||
Contact: "Chao Yu" <yuchao0@huawei.com>
|
||||
Description:
|
||||
Controls current reserved blocks in system.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/gc_urgent
|
||||
Date: August 2017
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Do background GC agressively
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/gc_urgent_sleep_time
|
||||
Date: August 2017
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls sleep time of GC urgent mode
|
||||
|
||||
@@ -157,6 +157,20 @@ data_flush Enable data flushing before checkpoint in order to
|
||||
mode=%s Control block allocation mode which supports "adaptive"
|
||||
and "lfs". In "lfs" mode, there should be no random
|
||||
writes towards main area.
|
||||
io_bits=%u Set the bit size of write IO requests. It should be set
|
||||
with "mode=lfs".
|
||||
usrquota Enable plain user disk quota accounting.
|
||||
grpquota Enable plain group disk quota accounting.
|
||||
prjquota Enable plain project quota accounting.
|
||||
usrjquota=<file> Appoint specified file and type during mount, so that quota
|
||||
grpjquota=<file> information can be properly updated during recovery flow,
|
||||
prjjquota=<file> <quota file>: must be in root directory;
|
||||
jqfmt=<quota type> <quota type>: [vfsold,vfsv0,vfsv1].
|
||||
offusrjquota Turn off user journelled quota.
|
||||
offgrpjquota Turn off group journelled quota.
|
||||
offprjjquota Turn off project journelled quota.
|
||||
quota Enable plain user disk quota accounting.
|
||||
noquota Disable all plain disk quota option.
|
||||
|
||||
================================================================================
|
||||
DEBUGFS ENTRIES
|
||||
@@ -202,6 +216,15 @@ Files in /sys/fs/f2fs/<devname>
|
||||
gc_idle = 1 will select the Cost Benefit approach
|
||||
& setting gc_idle = 2 will select the greedy approach.
|
||||
|
||||
gc_urgent This parameter controls triggering background GCs
|
||||
urgently or not. Setting gc_urgent = 0 [default]
|
||||
makes back to default behavior, while if it is set
|
||||
to 1, background thread starts to do GC by given
|
||||
gc_urgent_sleep_time interval.
|
||||
|
||||
gc_urgent_sleep_time This parameter controls sleep time for gc_urgent.
|
||||
500 ms is set by default. See above gc_urgent.
|
||||
|
||||
reclaim_segments This parameter controls the number of prefree
|
||||
segments to be reclaimed. If the number of prefree
|
||||
segments is larger than the number of segments
|
||||
|
||||
@@ -1151,8 +1151,21 @@ access the data:
|
||||
usage. This is called key->payload.rcu_data0. The following accessors
|
||||
wrap the RCU calls to this element:
|
||||
|
||||
rcu_assign_keypointer(struct key *key, void *data);
|
||||
void *rcu_dereference_key(struct key *key);
|
||||
(a) Set or change the first payload pointer:
|
||||
|
||||
rcu_assign_keypointer(struct key *key, void *data);
|
||||
|
||||
(b) Read the first payload pointer with the key semaphore held:
|
||||
|
||||
[const] void *dereference_key_locked([const] struct key *key);
|
||||
|
||||
Note that the return value will inherit its constness from the key
|
||||
parameter. Static analysis will give an error if it things the lock
|
||||
isn't held.
|
||||
|
||||
(c) Read the first payload pointer with the RCU read lock held:
|
||||
|
||||
const void *dereference_key_rcu(const struct key *key);
|
||||
|
||||
|
||||
===================
|
||||
|
||||
+1
-1
@@ -2432,7 +2432,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
|
||||
}
|
||||
|
||||
down_read(&key->sem);
|
||||
upayload = user_key_payload(key);
|
||||
upayload = user_key_payload_locked(key);
|
||||
if (IS_ERR_OR_NULL(upayload)) {
|
||||
rc = upayload ? PTR_ERR(upayload) : -EINVAL;
|
||||
goto out_key_put;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
config FS_ENCRYPTION
|
||||
tristate "FS Encryption (Per-file encryption)"
|
||||
depends on BLOCK
|
||||
select CRYPTO
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
@@ -8,9 +7,7 @@ config FS_ENCRYPTION
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
select ENCRYPTED_KEYS
|
||||
help
|
||||
Enable encryption of files and directories. This
|
||||
feature is similar to ecryptfs, but it is more memory
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
|
||||
|
||||
fscrypto-y := crypto.o fname.o policy.o keyinfo.o
|
||||
fscrypto-$(CONFIG_BLOCK) += bio.o
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* This contains encryption functions for per-file encryption.
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* Written by Michael Halcrow, 2014.
|
||||
*
|
||||
* Filename encryption additions
|
||||
* Uday Savagaonkar, 2014
|
||||
* Encryption policy handling additions
|
||||
* Ildar Muslukhov, 2014
|
||||
* Add fscrypt_pullback_bio_page()
|
||||
* Jaegeuk Kim, 2015.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*
|
||||
* The usage of AES-XTS should conform to recommendations in NIST
|
||||
* Special Publication 800-38E and IEEE P1619/D16.
|
||||
*/
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/namei.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/*
|
||||
* Call fscrypt_decrypt_page on every single page, reusing the encryption
|
||||
* context.
|
||||
*/
|
||||
static void completion_pages(struct work_struct *work)
|
||||
{
|
||||
struct fscrypt_ctx *ctx =
|
||||
container_of(work, struct fscrypt_ctx, r.work);
|
||||
struct bio *bio = ctx->r.bio;
|
||||
struct bio_vec *bv;
|
||||
int i;
|
||||
|
||||
bio_for_each_segment_all(bv, bio, i) {
|
||||
struct page *page = bv->bv_page;
|
||||
int ret = fscrypt_decrypt_page(page->mapping->host, page,
|
||||
PAGE_SIZE, 0, page->index);
|
||||
|
||||
if (ret) {
|
||||
WARN_ON_ONCE(1);
|
||||
SetPageError(page);
|
||||
} else {
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
unlock_page(page);
|
||||
}
|
||||
fscrypt_release_ctx(ctx);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio)
|
||||
{
|
||||
INIT_WORK(&ctx->r.work, completion_pages);
|
||||
ctx->r.bio = bio;
|
||||
queue_work(fscrypt_read_workqueue, &ctx->r.work);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_bio_pages);
|
||||
|
||||
void fscrypt_pullback_bio_page(struct page **page, bool restore)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *bounce_page;
|
||||
|
||||
/* The bounce data pages are unmapped. */
|
||||
if ((*page)->mapping)
|
||||
return;
|
||||
|
||||
/* The bounce data page is unmapped. */
|
||||
bounce_page = *page;
|
||||
ctx = (struct fscrypt_ctx *)page_private(bounce_page);
|
||||
|
||||
/* restore control page */
|
||||
*page = ctx->w.control_page;
|
||||
|
||||
if (restore)
|
||||
fscrypt_restore_control_page(bounce_page);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_pullback_bio_page);
|
||||
|
||||
int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct bio *bio;
|
||||
int ret, err = 0;
|
||||
|
||||
BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE);
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, GFP_NOFS);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT);
|
||||
if (IS_ERR(ciphertext_page)) {
|
||||
err = PTR_ERR(ciphertext_page);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), ciphertext_page,
|
||||
PAGE_SIZE, 0, GFP_NOFS);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
bio = bio_alloc(GFP_NOWAIT, 1);
|
||||
if (!bio) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector =
|
||||
pblk << (inode->i_sb->s_blocksize_bits - 9);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
ret = bio_add_page(bio, ciphertext_page,
|
||||
inode->i_sb->s_blocksize, 0);
|
||||
if (ret != inode->i_sb->s_blocksize) {
|
||||
/* should never happen! */
|
||||
WARN_ON(1);
|
||||
bio_put(bio);
|
||||
err = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
err = submit_bio_wait(bio);
|
||||
if (err == 0 && bio->bi_error)
|
||||
err = -EIO;
|
||||
bio_put(bio);
|
||||
if (err)
|
||||
goto errout;
|
||||
lblk++;
|
||||
pblk++;
|
||||
}
|
||||
err = 0;
|
||||
errout:
|
||||
fscrypt_release_ctx(ctx);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_zeroout_range);
|
||||
+100
-169
@@ -24,10 +24,10 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <crypto/aes.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
static unsigned int num_prealloc_crypto_pages = 32;
|
||||
static unsigned int num_prealloc_crypto_ctxs = 128;
|
||||
@@ -44,7 +44,7 @@ static mempool_t *fscrypt_bounce_page_pool = NULL;
|
||||
static LIST_HEAD(fscrypt_free_ctxs);
|
||||
static DEFINE_SPINLOCK(fscrypt_ctx_lock);
|
||||
|
||||
static struct workqueue_struct *fscrypt_read_workqueue;
|
||||
struct workqueue_struct *fscrypt_read_workqueue;
|
||||
static DEFINE_MUTEX(fscrypt_init_mutex);
|
||||
|
||||
static struct kmem_cache *fscrypt_ctx_cachep;
|
||||
@@ -63,7 +63,7 @@ void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (ctx->flags & FS_WRITE_PATH_FL && ctx->w.bounce_page) {
|
||||
if (ctx->flags & FS_CTX_HAS_BOUNCE_BUFFER_FL && ctx->w.bounce_page) {
|
||||
mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool);
|
||||
ctx->w.bounce_page = NULL;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ EXPORT_SYMBOL(fscrypt_release_ctx);
|
||||
* Return: An allocated and initialized encryption context on success; error
|
||||
* value or NULL otherwise.
|
||||
*/
|
||||
struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags)
|
||||
struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags)
|
||||
{
|
||||
struct fscrypt_ctx *ctx = NULL;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
@@ -121,7 +121,7 @@ struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags)
|
||||
} else {
|
||||
ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
|
||||
}
|
||||
ctx->flags &= ~FS_WRITE_PATH_FL;
|
||||
ctx->flags &= ~FS_CTX_HAS_BOUNCE_BUFFER_FL;
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_ctx);
|
||||
@@ -141,20 +141,15 @@ static void page_crypt_complete(struct crypto_async_request *req, int res)
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FS_DECRYPT = 0,
|
||||
FS_ENCRYPT,
|
||||
} fscrypt_direction_t;
|
||||
|
||||
static int do_page_crypto(struct inode *inode,
|
||||
fscrypt_direction_t rw, pgoff_t index,
|
||||
struct page *src_page, struct page *dest_page,
|
||||
gfp_t gfp_flags)
|
||||
int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw,
|
||||
u64 lblk_num, struct page *src_page,
|
||||
struct page *dest_page, unsigned int len,
|
||||
unsigned int offs, gfp_t gfp_flags)
|
||||
{
|
||||
struct {
|
||||
__le64 index;
|
||||
u8 padding[FS_XTS_TWEAK_SIZE - sizeof(__le64)];
|
||||
} xts_tweak;
|
||||
u8 padding[FS_IV_SIZE - sizeof(__le64)];
|
||||
} iv;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist dst, src;
|
||||
@@ -162,6 +157,18 @@ static int do_page_crypto(struct inode *inode,
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
|
||||
BUG_ON(len == 0);
|
||||
|
||||
BUILD_BUG_ON(sizeof(iv) != FS_IV_SIZE);
|
||||
BUILD_BUG_ON(AES_BLOCK_SIZE != FS_IV_SIZE);
|
||||
iv.index = cpu_to_le64(lblk_num);
|
||||
memset(iv.padding, 0, sizeof(iv.padding));
|
||||
|
||||
if (ci->ci_essiv_tfm != NULL) {
|
||||
crypto_cipher_encrypt_one(ci->ci_essiv_tfm, (u8 *)&iv,
|
||||
(u8 *)&iv);
|
||||
}
|
||||
|
||||
req = skcipher_request_alloc(tfm, gfp_flags);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
@@ -174,15 +181,11 @@ static int do_page_crypto(struct inode *inode,
|
||||
req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
page_crypt_complete, &ecr);
|
||||
|
||||
BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE);
|
||||
xts_tweak.index = cpu_to_le64(index);
|
||||
memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding));
|
||||
|
||||
sg_init_table(&dst, 1);
|
||||
sg_set_page(&dst, dest_page, PAGE_SIZE, 0);
|
||||
sg_set_page(&dst, dest_page, len, offs);
|
||||
sg_init_table(&src, 1);
|
||||
sg_set_page(&src, src_page, PAGE_SIZE, 0);
|
||||
skcipher_request_set_crypt(req, &src, &dst, PAGE_SIZE, &xts_tweak);
|
||||
sg_set_page(&src, src_page, len, offs);
|
||||
skcipher_request_set_crypt(req, &src, &dst, len, &iv);
|
||||
if (rw == FS_DECRYPT)
|
||||
res = crypto_skcipher_decrypt(req);
|
||||
else
|
||||
@@ -202,53 +205,86 @@ static int do_page_crypto(struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags)
|
||||
struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags);
|
||||
if (ctx->w.bounce_page == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= FS_WRITE_PATH_FL;
|
||||
ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL;
|
||||
return ctx->w.bounce_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscypt_encrypt_page() - Encrypts a page
|
||||
* @inode: The inode for which the encryption should take place
|
||||
* @plaintext_page: The page to encrypt. Must be locked.
|
||||
* @gfp_flags: The gfp flag for memory allocation
|
||||
* @inode: The inode for which the encryption should take place
|
||||
* @page: The page to encrypt. Must be locked for bounce-page
|
||||
* encryption.
|
||||
* @len: Length of data to encrypt in @page and encrypted
|
||||
* data in returned page.
|
||||
* @offs: Offset of data within @page and returned
|
||||
* page holding encrypted data.
|
||||
* @lblk_num: Logical block number. This must be unique for multiple
|
||||
* calls with same inode, except when overwriting
|
||||
* previously written data.
|
||||
* @gfp_flags: The gfp flag for memory allocation
|
||||
*
|
||||
* Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
|
||||
* encryption context.
|
||||
* Encrypts @page using the ctx encryption context. Performs encryption
|
||||
* either in-place or into a newly allocated bounce page.
|
||||
* Called on the page write path.
|
||||
*
|
||||
* Called on the page write path. The caller must call
|
||||
* Bounce page allocation is the default.
|
||||
* In this case, the contents of @page are encrypted and stored in an
|
||||
* allocated bounce page. @page has to be locked and the caller must call
|
||||
* fscrypt_restore_control_page() on the returned ciphertext page to
|
||||
* release the bounce buffer and the encryption context.
|
||||
*
|
||||
* Return: An allocated page with the encrypted content on success. Else, an
|
||||
* In-place encryption is used by setting the FS_CFLG_OWN_PAGES flag in
|
||||
* fscrypt_operations. Here, the input-page is returned with its content
|
||||
* encrypted.
|
||||
*
|
||||
* Return: A page with the encrypted content on success. Else, an
|
||||
* error value or NULL.
|
||||
*/
|
||||
struct page *fscrypt_encrypt_page(struct inode *inode,
|
||||
struct page *plaintext_page, gfp_t gfp_flags)
|
||||
struct page *fscrypt_encrypt_page(const struct inode *inode,
|
||||
struct page *page,
|
||||
unsigned int len,
|
||||
unsigned int offs,
|
||||
u64 lblk_num, gfp_t gfp_flags)
|
||||
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct page *ciphertext_page = page;
|
||||
int err;
|
||||
|
||||
BUG_ON(!PageLocked(plaintext_page));
|
||||
BUG_ON(len % FS_CRYPTO_BLOCK_SIZE != 0);
|
||||
|
||||
if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) {
|
||||
/* with inplace-encryption we just encrypt the page */
|
||||
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page,
|
||||
ciphertext_page, len, offs,
|
||||
gfp_flags);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return ciphertext_page;
|
||||
}
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, gfp_flags);
|
||||
if (IS_ERR(ctx))
|
||||
return (struct page *)ctx;
|
||||
|
||||
/* The encryption operation will require a bounce page. */
|
||||
ciphertext_page = alloc_bounce_page(ctx, gfp_flags);
|
||||
ciphertext_page = fscrypt_alloc_bounce_page(ctx, gfp_flags);
|
||||
if (IS_ERR(ciphertext_page))
|
||||
goto errout;
|
||||
|
||||
ctx->w.control_page = plaintext_page;
|
||||
err = do_page_crypto(inode, FS_ENCRYPT, plaintext_page->index,
|
||||
plaintext_page, ciphertext_page,
|
||||
gfp_flags);
|
||||
ctx->w.control_page = page;
|
||||
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num,
|
||||
page, ciphertext_page, len, offs,
|
||||
gfp_flags);
|
||||
if (err) {
|
||||
ciphertext_page = ERR_PTR(err);
|
||||
goto errout;
|
||||
@@ -265,8 +301,13 @@ errout:
|
||||
EXPORT_SYMBOL(fscrypt_encrypt_page);
|
||||
|
||||
/**
|
||||
* f2crypt_decrypt_page() - Decrypts a page in-place
|
||||
* @page: The page to decrypt. Must be locked.
|
||||
* fscrypt_decrypt_page() - Decrypts a page in-place
|
||||
* @inode: The corresponding inode for the page to decrypt.
|
||||
* @page: The page to decrypt. Must be locked in case
|
||||
* it is a writeback page (FS_CFLG_OWN_PAGES unset).
|
||||
* @len: Number of bytes in @page to be decrypted.
|
||||
* @offs: Start of data in @page.
|
||||
* @lblk_num: Logical block number.
|
||||
*
|
||||
* Decrypts page in-place using the ctx encryption context.
|
||||
*
|
||||
@@ -274,76 +315,17 @@ EXPORT_SYMBOL(fscrypt_encrypt_page);
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise.
|
||||
*/
|
||||
int fscrypt_decrypt_page(struct page *page)
|
||||
int fscrypt_decrypt_page(const struct inode *inode, struct page *page,
|
||||
unsigned int len, unsigned int offs, u64 lblk_num)
|
||||
{
|
||||
BUG_ON(!PageLocked(page));
|
||||
if (!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES))
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
return do_page_crypto(page->mapping->host,
|
||||
FS_DECRYPT, page->index, page, page, GFP_NOFS);
|
||||
return fscrypt_do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page,
|
||||
len, offs, GFP_NOFS);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_page);
|
||||
|
||||
int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct bio *bio;
|
||||
int ret, err = 0;
|
||||
|
||||
BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE);
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, GFP_NOFS);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT);
|
||||
if (IS_ERR(ciphertext_page)) {
|
||||
err = PTR_ERR(ciphertext_page);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
err = do_page_crypto(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), ciphertext_page,
|
||||
GFP_NOFS);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
bio = bio_alloc(GFP_NOWAIT, 1);
|
||||
if (!bio) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector =
|
||||
pblk << (inode->i_sb->s_blocksize_bits - 9);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
ret = bio_add_page(bio, ciphertext_page,
|
||||
inode->i_sb->s_blocksize, 0);
|
||||
if (ret != inode->i_sb->s_blocksize) {
|
||||
/* should never happen! */
|
||||
WARN_ON(1);
|
||||
bio_put(bio);
|
||||
err = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
err = submit_bio_wait(bio);
|
||||
if ((err == 0) && bio->bi_error)
|
||||
err = -EIO;
|
||||
bio_put(bio);
|
||||
if (err)
|
||||
goto errout;
|
||||
lblk++;
|
||||
pblk++;
|
||||
}
|
||||
err = 0;
|
||||
errout:
|
||||
fscrypt_release_ctx(ctx);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_zeroout_range);
|
||||
|
||||
/*
|
||||
* Validate dentries for encrypted directories to make sure we aren't
|
||||
* potentially caching stale data after a key has been added or
|
||||
@@ -392,63 +374,6 @@ const struct dentry_operations fscrypt_d_ops = {
|
||||
};
|
||||
EXPORT_SYMBOL(fscrypt_d_ops);
|
||||
|
||||
/*
|
||||
* Call fscrypt_decrypt_page on every single page, reusing the encryption
|
||||
* context.
|
||||
*/
|
||||
static void completion_pages(struct work_struct *work)
|
||||
{
|
||||
struct fscrypt_ctx *ctx =
|
||||
container_of(work, struct fscrypt_ctx, r.work);
|
||||
struct bio *bio = ctx->r.bio;
|
||||
struct bio_vec *bv;
|
||||
int i;
|
||||
|
||||
bio_for_each_segment_all(bv, bio, i) {
|
||||
struct page *page = bv->bv_page;
|
||||
int ret = fscrypt_decrypt_page(page);
|
||||
|
||||
if (ret) {
|
||||
WARN_ON_ONCE(1);
|
||||
SetPageError(page);
|
||||
} else {
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
unlock_page(page);
|
||||
}
|
||||
fscrypt_release_ctx(ctx);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio)
|
||||
{
|
||||
INIT_WORK(&ctx->r.work, completion_pages);
|
||||
ctx->r.bio = bio;
|
||||
queue_work(fscrypt_read_workqueue, &ctx->r.work);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_bio_pages);
|
||||
|
||||
void fscrypt_pullback_bio_page(struct page **page, bool restore)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *bounce_page;
|
||||
|
||||
/* The bounce data pages are unmapped. */
|
||||
if ((*page)->mapping)
|
||||
return;
|
||||
|
||||
/* The bounce data page is unmapped. */
|
||||
bounce_page = *page;
|
||||
ctx = (struct fscrypt_ctx *)page_private(bounce_page);
|
||||
|
||||
/* restore control page */
|
||||
*page = ctx->w.control_page;
|
||||
|
||||
if (restore)
|
||||
fscrypt_restore_control_page(bounce_page);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_pullback_bio_page);
|
||||
|
||||
void fscrypt_restore_control_page(struct page *page)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
@@ -474,17 +399,22 @@ static void fscrypt_destroy(void)
|
||||
|
||||
/**
|
||||
* fscrypt_initialize() - allocate major buffers for fs encryption.
|
||||
* @cop_flags: fscrypt operations flags
|
||||
*
|
||||
* We only call this when we start accessing encrypted files, since it
|
||||
* results in memory getting allocated that wouldn't otherwise be used.
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise.
|
||||
*/
|
||||
int fscrypt_initialize(void)
|
||||
int fscrypt_initialize(unsigned int cop_flags)
|
||||
{
|
||||
int i, res = -ENOMEM;
|
||||
|
||||
if (fscrypt_bounce_page_pool)
|
||||
/*
|
||||
* No need to allocate a bounce page pool if there already is one or
|
||||
* this FS won't use it.
|
||||
*/
|
||||
if (cop_flags & FS_CFLG_OWN_PAGES || fscrypt_bounce_page_pool)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&fscrypt_init_mutex);
|
||||
@@ -513,7 +443,6 @@ fail:
|
||||
mutex_unlock(&fscrypt_init_mutex);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_initialize);
|
||||
|
||||
/**
|
||||
* fscrypt_init() - Set up for fs encryption.
|
||||
@@ -555,6 +484,8 @@ static void __exit fscrypt_exit(void)
|
||||
destroy_workqueue(fscrypt_read_workqueue);
|
||||
kmem_cache_destroy(fscrypt_ctx_cachep);
|
||||
kmem_cache_destroy(fscrypt_info_cachep);
|
||||
|
||||
fscrypt_essiv_cleanup();
|
||||
}
|
||||
module_exit(fscrypt_exit);
|
||||
|
||||
|
||||
+75
-34
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/**
|
||||
* fname_crypt_complete() - completion callback for filename crypto
|
||||
@@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode,
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
|
||||
|
||||
/**
|
||||
* digest_encode() -
|
||||
*
|
||||
@@ -209,7 +211,7 @@ static int digest_decode(const char *src, int len, char *dst)
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||
u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen)
|
||||
{
|
||||
int padding = 32;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
@@ -227,14 +229,17 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
|
||||
* Allocates an output buffer that is sufficient for the crypto operation
|
||||
* specified by the context and the direction.
|
||||
*/
|
||||
int fscrypt_fname_alloc_buffer(struct inode *inode,
|
||||
int fscrypt_fname_alloc_buffer(const struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
u32 olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
const u32 max_encoded_len =
|
||||
max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
|
||||
|
||||
crypto_str->len = olen;
|
||||
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
olen = max(olen, max_encoded_len);
|
||||
|
||||
/*
|
||||
* Allocated buffer can hold one more character to null-terminate the
|
||||
* string
|
||||
@@ -266,6 +271,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer);
|
||||
*
|
||||
* The caller must have allocated sufficient memory for the @oname string.
|
||||
*
|
||||
* If the key is available, we'll decrypt the disk name; otherwise, we'll encode
|
||||
* it for presentation. Short names are directly base64-encoded, while long
|
||||
* names are encoded in fscrypt_digested_name format.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
@@ -274,7 +283,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
char buf[24];
|
||||
struct fscrypt_digested_name digested_name;
|
||||
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
@@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
if (inode->i_crypt_info)
|
||||
return fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) {
|
||||
oname->len = digest_encode(iname->name, iname->len,
|
||||
oname->name);
|
||||
return 0;
|
||||
}
|
||||
if (hash) {
|
||||
memcpy(buf, &hash, 4);
|
||||
memcpy(buf + 4, &minor_hash, 4);
|
||||
digested_name.hash = hash;
|
||||
digested_name.minor_hash = minor_hash;
|
||||
} else {
|
||||
memset(buf, 0, 8);
|
||||
digested_name.hash = 0;
|
||||
digested_name.minor_hash = 0;
|
||||
}
|
||||
memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
|
||||
memcpy(digested_name.digest,
|
||||
FSCRYPT_FNAME_DIGEST(iname->name, iname->len),
|
||||
FSCRYPT_FNAME_DIGEST_SIZE);
|
||||
oname->name[0] = '_';
|
||||
oname->len = 1 + digest_encode(buf, 24, oname->name + 1);
|
||||
oname->len = 1 + digest_encode((const char *)&digested_name,
|
||||
sizeof(digested_name), oname->name + 1);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
||||
@@ -332,14 +345,39 @@ int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
* in a directory. Consequently, a user space name cannot be mapped to
|
||||
* a disk-space name
|
||||
*/
|
||||
return -EACCES;
|
||||
return -ENOKEY;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||
|
||||
/**
|
||||
* fscrypt_setup_filename() - prepare to search a possibly encrypted directory
|
||||
* @dir: the directory that will be searched
|
||||
* @iname: the user-provided filename being searched for
|
||||
* @lookup: 1 if we're allowed to proceed without the key because it's
|
||||
* ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot
|
||||
* proceed without the key because we're going to create the dir_entry.
|
||||
* @fname: the filename information to be filled in
|
||||
*
|
||||
* Given a user-provided filename @iname, this function sets @fname->disk_name
|
||||
* to the name that would be stored in the on-disk directory entry, if possible.
|
||||
* If the directory is unencrypted this is simply @iname. Else, if we have the
|
||||
* directory's encryption key, then @iname is the plaintext, so we encrypt it to
|
||||
* get the disk_name.
|
||||
*
|
||||
* Else, for keyless @lookup operations, @iname is the presented ciphertext, so
|
||||
* we decode it to get either the ciphertext disk_name (for short names) or the
|
||||
* fscrypt_digested_name (for long names). Non-@lookup operations will be
|
||||
* impossible in this case, so we fail them with ENOKEY.
|
||||
*
|
||||
* If successful, fscrypt_free_filename() must be called later to clean up.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
int ret = 0, bigname = 0;
|
||||
int ret;
|
||||
int digested;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
@@ -367,31 +405,43 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
return 0;
|
||||
}
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
return -ENOKEY;
|
||||
|
||||
/*
|
||||
* We don't have the key and we are doing a lookup; decode the
|
||||
* user-supplied name
|
||||
*/
|
||||
if (iname->name[0] == '_')
|
||||
bigname = 1;
|
||||
if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
|
||||
return -ENOENT;
|
||||
if (iname->name[0] == '_') {
|
||||
if (iname->len !=
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)))
|
||||
return -ENOENT;
|
||||
digested = 1;
|
||||
} else {
|
||||
if (iname->len >
|
||||
BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE))
|
||||
return -ENOENT;
|
||||
digested = 0;
|
||||
}
|
||||
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
fname->crypto_buf.name =
|
||||
kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE,
|
||||
sizeof(struct fscrypt_digested_name)),
|
||||
GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
ret = digest_decode(iname->name + digested, iname->len - digested,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (bigname) {
|
||||
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
||||
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
|
||||
if (digested) {
|
||||
const struct fscrypt_digested_name *n =
|
||||
(const void *)fname->crypto_buf.name;
|
||||
fname->hash = n->hash;
|
||||
fname->minor_hash = n->minor_hash;
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
@@ -403,12 +453,3 @@ errout:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_setup_filename);
|
||||
|
||||
void fscrypt_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_free_filename);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* fscrypt_private.h
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption key functions.
|
||||
*
|
||||
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _FSCRYPT_PRIVATE_H
|
||||
#define _FSCRYPT_PRIVATE_H
|
||||
|
||||
#include <linux/fscrypt_supp.h>
|
||||
#include <crypto/hash.h>
|
||||
|
||||
/* Encryption parameters */
|
||||
#define FS_IV_SIZE 16
|
||||
#define FS_AES_128_ECB_KEY_SIZE 16
|
||||
#define FS_AES_128_CBC_KEY_SIZE 16
|
||||
#define FS_AES_128_CTS_KEY_SIZE 16
|
||||
#define FS_AES_256_GCM_KEY_SIZE 32
|
||||
#define FS_AES_256_CBC_KEY_SIZE 32
|
||||
#define FS_AES_256_CTS_KEY_SIZE 32
|
||||
#define FS_AES_256_XTS_KEY_SIZE 64
|
||||
|
||||
#define FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
/**
|
||||
* Encryption context for inode
|
||||
*
|
||||
* Protector format:
|
||||
* 1 byte: Protector format (1 = this version)
|
||||
* 1 byte: File contents encryption mode
|
||||
* 1 byte: File names encryption mode
|
||||
* 1 byte: Flags
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct fscrypt_context {
|
||||
u8 format;
|
||||
u8 contents_encryption_mode;
|
||||
u8 filenames_encryption_mode;
|
||||
u8 flags;
|
||||
u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
|
||||
/*
|
||||
* A pointer to this structure is stored in the file system's in-core
|
||||
* representation of an inode.
|
||||
*/
|
||||
struct fscrypt_info {
|
||||
u8 ci_data_mode;
|
||||
u8 ci_filename_mode;
|
||||
u8 ci_flags;
|
||||
struct crypto_skcipher *ci_ctfm;
|
||||
struct crypto_cipher *ci_essiv_tfm;
|
||||
u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FS_DECRYPT = 0,
|
||||
FS_ENCRYPT,
|
||||
} fscrypt_direction_t;
|
||||
|
||||
#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002
|
||||
|
||||
struct fscrypt_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_FS_COMPLETION_RESULT(ecr) \
|
||||
struct fscrypt_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER_ONSTACK((ecr).completion), 0 }
|
||||
|
||||
|
||||
/* crypto.c */
|
||||
extern int fscrypt_initialize(unsigned int cop_flags);
|
||||
extern struct workqueue_struct *fscrypt_read_workqueue;
|
||||
extern int fscrypt_do_page_crypto(const struct inode *inode,
|
||||
fscrypt_direction_t rw, u64 lblk_num,
|
||||
struct page *src_page,
|
||||
struct page *dest_page,
|
||||
unsigned int len, unsigned int offs,
|
||||
gfp_t gfp_flags);
|
||||
extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
|
||||
gfp_t gfp_flags);
|
||||
|
||||
/* keyinfo.c */
|
||||
extern void __exit fscrypt_essiv_cleanup(void);
|
||||
|
||||
#endif /* _FSCRYPT_PRIVATE_H */
|
||||
+152
-70
@@ -10,7 +10,12 @@
|
||||
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/sha.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
static struct crypto_shash *essiv_hash_tfm;
|
||||
|
||||
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
{
|
||||
@@ -27,13 +32,13 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
* derive_key_aes() - Derive a key using AES-128-ECB
|
||||
* @deriving_key: Encryption key used for derivation.
|
||||
* @source_key: Source key to which to apply derivation.
|
||||
* @derived_key: Derived key.
|
||||
* @derived_raw_key: Derived raw key.
|
||||
*
|
||||
* Return: Zero on success; non-zero otherwise.
|
||||
*/
|
||||
static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
|
||||
u8 source_key[FS_AES_256_XTS_KEY_SIZE],
|
||||
u8 derived_key[FS_AES_256_XTS_KEY_SIZE])
|
||||
const struct fscrypt_key *source_key,
|
||||
u8 derived_raw_key[FS_MAX_KEY_SIZE])
|
||||
{
|
||||
int res = 0;
|
||||
struct skcipher_request *req = NULL;
|
||||
@@ -60,10 +65,10 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
sg_init_one(&src_sg, source_key, FS_AES_256_XTS_KEY_SIZE);
|
||||
sg_init_one(&dst_sg, derived_key, FS_AES_256_XTS_KEY_SIZE);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
FS_AES_256_XTS_KEY_SIZE, NULL);
|
||||
sg_init_one(&src_sg, source_key->raw, source_key->size);
|
||||
sg_init_one(&dst_sg, derived_raw_key, source_key->size);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size,
|
||||
NULL);
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
@@ -77,26 +82,22 @@ out:
|
||||
|
||||
static int validate_user_key(struct fscrypt_info *crypt_info,
|
||||
struct fscrypt_context *ctx, u8 *raw_key,
|
||||
u8 *prefix, int prefix_size)
|
||||
const char *prefix, int min_keysize)
|
||||
{
|
||||
u8 *full_key_descriptor;
|
||||
char *description;
|
||||
struct key *keyring_key;
|
||||
struct fscrypt_key *master_key;
|
||||
const struct user_key_payload *ukp;
|
||||
int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1;
|
||||
int res;
|
||||
|
||||
full_key_descriptor = kmalloc(full_key_len, GFP_NOFS);
|
||||
if (!full_key_descriptor)
|
||||
description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
|
||||
FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx->master_key_descriptor);
|
||||
if (!description)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(full_key_descriptor, prefix, prefix_size);
|
||||
sprintf(full_key_descriptor + prefix_size,
|
||||
"%*phN", FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx->master_key_descriptor);
|
||||
full_key_descriptor[full_key_len - 1] = '\0';
|
||||
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
|
||||
kfree(full_key_descriptor);
|
||||
keyring_key = request_key(&key_type_logon, description, NULL);
|
||||
kfree(description);
|
||||
if (IS_ERR(keyring_key))
|
||||
return PTR_ERR(keyring_key);
|
||||
down_read(&keyring_key->sem);
|
||||
@@ -107,7 +108,7 @@ static int validate_user_key(struct fscrypt_info *crypt_info,
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
ukp = user_key_payload(keyring_key);
|
||||
ukp = user_key_payload_locked(keyring_key);
|
||||
if (ukp->datalen != sizeof(struct fscrypt_key)) {
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
@@ -115,50 +116,60 @@ static int validate_user_key(struct fscrypt_info *crypt_info,
|
||||
master_key = (struct fscrypt_key *)ukp->data;
|
||||
BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
if (master_key->size != FS_AES_256_XTS_KEY_SIZE) {
|
||||
if (master_key->size < min_keysize || master_key->size > FS_MAX_KEY_SIZE
|
||||
|| master_key->size % AES_BLOCK_SIZE != 0) {
|
||||
printk_once(KERN_WARNING
|
||||
"%s: key size incorrect: %d\n",
|
||||
__func__, master_key->size);
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
res = derive_key_aes(ctx->nonce, master_key->raw, raw_key);
|
||||
res = derive_key_aes(ctx->nonce, master_key, raw_key);
|
||||
out:
|
||||
up_read(&keyring_key->sem);
|
||||
key_put(keyring_key);
|
||||
return res;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *cipher_str;
|
||||
int keysize;
|
||||
} available_modes[] = {
|
||||
[FS_ENCRYPTION_MODE_AES_256_XTS] = { "xts(aes)",
|
||||
FS_AES_256_XTS_KEY_SIZE },
|
||||
[FS_ENCRYPTION_MODE_AES_256_CTS] = { "cts(cbc(aes))",
|
||||
FS_AES_256_CTS_KEY_SIZE },
|
||||
[FS_ENCRYPTION_MODE_AES_128_CBC] = { "cbc(aes)",
|
||||
FS_AES_128_CBC_KEY_SIZE },
|
||||
[FS_ENCRYPTION_MODE_AES_128_CTS] = { "cts(cbc(aes))",
|
||||
FS_AES_128_CTS_KEY_SIZE },
|
||||
};
|
||||
|
||||
static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode,
|
||||
const char **cipher_str_ret, int *keysize_ret)
|
||||
{
|
||||
u32 mode;
|
||||
|
||||
if (!fscrypt_valid_enc_modes(ci->ci_data_mode, ci->ci_filename_mode)) {
|
||||
pr_warn_ratelimited("fscrypt: inode %lu uses unsupported encryption modes (contents mode %d, filenames mode %d)\n",
|
||||
inode->i_ino,
|
||||
ci->ci_data_mode, ci->ci_filename_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
if (ci->ci_data_mode == FS_ENCRYPTION_MODE_AES_256_XTS) {
|
||||
*cipher_str_ret = "xts(aes)";
|
||||
*keysize_ret = FS_AES_256_XTS_KEY_SIZE;
|
||||
return 0;
|
||||
}
|
||||
pr_warn_once("fscrypto: unsupported contents encryption mode "
|
||||
"%d for inode %lu\n",
|
||||
ci->ci_data_mode, inode->i_ino);
|
||||
return -ENOKEY;
|
||||
mode = ci->ci_data_mode;
|
||||
} else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
|
||||
mode = ci->ci_filename_mode;
|
||||
} else {
|
||||
WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n",
|
||||
inode->i_ino, (inode->i_mode & S_IFMT));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
|
||||
if (ci->ci_filename_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
|
||||
*cipher_str_ret = "cts(cbc(aes))";
|
||||
*keysize_ret = FS_AES_256_CTS_KEY_SIZE;
|
||||
return 0;
|
||||
}
|
||||
pr_warn_once("fscrypto: unsupported filenames encryption mode "
|
||||
"%d for inode %lu\n",
|
||||
ci->ci_filename_mode, inode->i_ino);
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
pr_warn_once("fscrypto: unsupported file type %d for inode %lu\n",
|
||||
(inode->i_mode & S_IFMT), inode->i_ino);
|
||||
return -ENOKEY;
|
||||
*cipher_str_ret = available_modes[mode].cipher_str;
|
||||
*keysize_ret = available_modes[mode].keysize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_crypt_info(struct fscrypt_info *ci)
|
||||
@@ -167,9 +178,76 @@ static void put_crypt_info(struct fscrypt_info *ci)
|
||||
return;
|
||||
|
||||
crypto_free_skcipher(ci->ci_ctfm);
|
||||
crypto_free_cipher(ci->ci_essiv_tfm);
|
||||
kmem_cache_free(fscrypt_info_cachep, ci);
|
||||
}
|
||||
|
||||
static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt)
|
||||
{
|
||||
struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm);
|
||||
|
||||
/* init hash transform on demand */
|
||||
if (unlikely(!tfm)) {
|
||||
struct crypto_shash *prev_tfm;
|
||||
|
||||
tfm = crypto_alloc_shash("sha256", 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_warn_ratelimited("fscrypt: error allocating SHA-256 transform: %ld\n",
|
||||
PTR_ERR(tfm));
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
prev_tfm = cmpxchg(&essiv_hash_tfm, NULL, tfm);
|
||||
if (prev_tfm) {
|
||||
crypto_free_shash(tfm);
|
||||
tfm = prev_tfm;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SHASH_DESC_ON_STACK(desc, tfm);
|
||||
desc->tfm = tfm;
|
||||
desc->flags = 0;
|
||||
|
||||
return crypto_shash_digest(desc, key, keysize, salt);
|
||||
}
|
||||
}
|
||||
|
||||
static int init_essiv_generator(struct fscrypt_info *ci, const u8 *raw_key,
|
||||
int keysize)
|
||||
{
|
||||
int err;
|
||||
struct crypto_cipher *essiv_tfm;
|
||||
u8 salt[SHA256_DIGEST_SIZE];
|
||||
|
||||
essiv_tfm = crypto_alloc_cipher("aes", 0, 0);
|
||||
if (IS_ERR(essiv_tfm))
|
||||
return PTR_ERR(essiv_tfm);
|
||||
|
||||
ci->ci_essiv_tfm = essiv_tfm;
|
||||
|
||||
err = derive_essiv_salt(raw_key, keysize, salt);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Using SHA256 to derive the salt/key will result in AES-256 being
|
||||
* used for IV generation. File contents encryption will still use the
|
||||
* configured keysize (AES-128) nevertheless.
|
||||
*/
|
||||
err = crypto_cipher_setkey(essiv_tfm, salt, sizeof(salt));
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
memzero_explicit(salt, sizeof(salt));
|
||||
return err;
|
||||
}
|
||||
|
||||
void __exit fscrypt_essiv_cleanup(void)
|
||||
{
|
||||
crypto_free_shash(essiv_hash_tfm);
|
||||
}
|
||||
|
||||
int fscrypt_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct fscrypt_info *crypt_info;
|
||||
@@ -183,21 +261,21 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
||||
if (inode->i_crypt_info)
|
||||
return 0;
|
||||
|
||||
res = fscrypt_initialize();
|
||||
res = fscrypt_initialize(inode->i_sb->s_cop->flags);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res < 0) {
|
||||
if (!fscrypt_dummy_context_enabled(inode))
|
||||
if (!fscrypt_dummy_context_enabled(inode) ||
|
||||
inode->i_sb->s_cop->is_encrypted(inode))
|
||||
return res;
|
||||
/* Fake up a context for an unencrypted directory */
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE);
|
||||
} else if (res != sizeof(ctx)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -216,6 +294,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
||||
crypt_info->ci_data_mode = ctx.contents_encryption_mode;
|
||||
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
|
||||
crypt_info->ci_ctfm = NULL;
|
||||
crypt_info->ci_essiv_tfm = NULL;
|
||||
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
|
||||
sizeof(crypt_info->ci_master_key));
|
||||
|
||||
@@ -232,20 +311,12 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
||||
if (!raw_key)
|
||||
goto out;
|
||||
|
||||
if (fscrypt_dummy_context_enabled(inode)) {
|
||||
memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
|
||||
goto got_key;
|
||||
}
|
||||
|
||||
res = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
|
||||
res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX,
|
||||
keysize);
|
||||
if (res && inode->i_sb->s_cop->key_prefix) {
|
||||
u8 *prefix = NULL;
|
||||
int prefix_size, res2;
|
||||
|
||||
prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
|
||||
res2 = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
prefix, prefix_size);
|
||||
int res2 = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
inode->i_sb->s_cop->key_prefix,
|
||||
keysize);
|
||||
if (res2) {
|
||||
if (res2 == -ENOKEY)
|
||||
res = -ENOKEY;
|
||||
@@ -254,22 +325,33 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
||||
} else if (res) {
|
||||
goto out;
|
||||
}
|
||||
got_key:
|
||||
ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
|
||||
if (!ctfm || IS_ERR(ctfm)) {
|
||||
res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
|
||||
printk(KERN_DEBUG
|
||||
"%s: error %d (inode %u) allocating crypto tfm\n",
|
||||
__func__, res, (unsigned) inode->i_ino);
|
||||
pr_debug("%s: error %d (inode %lu) allocating crypto tfm\n",
|
||||
__func__, res, inode->i_ino);
|
||||
goto out;
|
||||
}
|
||||
crypt_info->ci_ctfm = ctfm;
|
||||
crypto_skcipher_clear_flags(ctfm, ~0);
|
||||
crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
/*
|
||||
* if the provided key is longer than keysize, we use the first
|
||||
* keysize bytes of the derived key only
|
||||
*/
|
||||
res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
if (S_ISREG(inode->i_mode) &&
|
||||
crypt_info->ci_data_mode == FS_ENCRYPTION_MODE_AES_128_CBC) {
|
||||
res = init_essiv_generator(crypt_info, raw_key, keysize);
|
||||
if (res) {
|
||||
pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n",
|
||||
__func__, res, inode->i_ino);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
|
||||
crypt_info = NULL;
|
||||
out:
|
||||
|
||||
+56
-95
@@ -10,76 +10,37 @@
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
static int inode_has_encryption_context(struct inode *inode)
|
||||
{
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return 0;
|
||||
return (inode->i_sb->s_cop->get_context(inode, NULL, 0L) > 0);
|
||||
}
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/*
|
||||
* check whether the policy is consistent with the encryption context
|
||||
* for the inode
|
||||
* check whether an encryption policy is consistent with an encryption context
|
||||
*/
|
||||
static int is_encryption_context_consistent_with_policy(struct inode *inode,
|
||||
static bool is_encryption_context_consistent_with_policy(
|
||||
const struct fscrypt_context *ctx,
|
||||
const struct fscrypt_policy *policy)
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return 0;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return 0;
|
||||
|
||||
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(ctx.flags == policy->flags) &&
|
||||
(ctx.contents_encryption_mode ==
|
||||
policy->contents_encryption_mode) &&
|
||||
(ctx.filenames_encryption_mode ==
|
||||
policy->filenames_encryption_mode));
|
||||
return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(ctx->flags == policy->flags) &&
|
||||
(ctx->contents_encryption_mode ==
|
||||
policy->contents_encryption_mode) &&
|
||||
(ctx->filenames_encryption_mode ==
|
||||
policy->filenames_encryption_mode);
|
||||
}
|
||||
|
||||
static int create_encryption_context_from_policy(struct inode *inode,
|
||||
const struct fscrypt_policy *policy)
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->set_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (inode->i_sb->s_cop->prepare_context) {
|
||||
res = inode->i_sb->s_cop->prepare_context(inode);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
if (!fscrypt_valid_contents_enc_mode(
|
||||
policy->contents_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid contents encryption mode %d\n", __func__,
|
||||
policy->contents_encryption_mode);
|
||||
if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
|
||||
policy->filenames_encryption_mode))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!fscrypt_valid_filenames_enc_mode(
|
||||
policy->filenames_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid filenames encryption mode %d\n", __func__,
|
||||
policy->filenames_encryption_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (policy->flags & ~FS_POLICY_FLAGS_VALID)
|
||||
return -EINVAL;
|
||||
@@ -93,16 +54,20 @@ static int create_encryption_context_from_policy(struct inode *inode,
|
||||
return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL);
|
||||
}
|
||||
|
||||
int fscrypt_process_policy(struct file *filp,
|
||||
const struct fscrypt_policy *policy)
|
||||
int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
|
||||
{
|
||||
struct fscrypt_policy policy;
|
||||
struct inode *inode = file_inode(filp);
|
||||
int ret;
|
||||
struct fscrypt_context ctx;
|
||||
|
||||
if (copy_from_user(&policy, arg, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EACCES;
|
||||
|
||||
if (policy->version != 0)
|
||||
if (policy.version != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
@@ -111,22 +76,23 @@ int fscrypt_process_policy(struct file *filp,
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
if (!inode_has_encryption_context(inode)) {
|
||||
ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (ret == -ENODATA) {
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
ret = -EINVAL;
|
||||
else if (!inode->i_sb->s_cop->empty_dir)
|
||||
ret = -EOPNOTSUPP;
|
||||
ret = -ENOTDIR;
|
||||
else if (!inode->i_sb->s_cop->empty_dir(inode))
|
||||
ret = -ENOTEMPTY;
|
||||
else
|
||||
ret = create_encryption_context_from_policy(inode,
|
||||
policy);
|
||||
} else if (!is_encryption_context_consistent_with_policy(inode,
|
||||
policy)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Policy inconsistent with encryption context\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
&policy);
|
||||
} else if (ret == sizeof(ctx) &&
|
||||
is_encryption_context_consistent_with_policy(&ctx,
|
||||
&policy)) {
|
||||
/* The file already uses the same encryption policy. */
|
||||
ret = 0;
|
||||
} else if (ret >= 0 || ret == -ERANGE) {
|
||||
/* The file already uses a different encryption policy. */
|
||||
ret = -EEXIST;
|
||||
}
|
||||
|
||||
inode_unlock(inode);
|
||||
@@ -134,32 +100,38 @@ int fscrypt_process_policy(struct file *filp,
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_process_policy);
|
||||
EXPORT_SYMBOL(fscrypt_ioctl_set_policy);
|
||||
|
||||
int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy)
|
||||
int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct fscrypt_context ctx;
|
||||
struct fscrypt_policy policy;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context ||
|
||||
!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
if (!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
return -ENODATA;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res < 0 && res != -ERANGE)
|
||||
return res;
|
||||
if (res != sizeof(ctx))
|
||||
return -ENODATA;
|
||||
return -EINVAL;
|
||||
if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
|
||||
return -EINVAL;
|
||||
|
||||
policy->version = 0;
|
||||
policy->contents_encryption_mode = ctx.contents_encryption_mode;
|
||||
policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
|
||||
policy->flags = ctx.flags;
|
||||
memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
|
||||
policy.version = 0;
|
||||
policy.contents_encryption_mode = ctx.contents_encryption_mode;
|
||||
policy.filenames_encryption_mode = ctx.filenames_encryption_mode;
|
||||
policy.flags = ctx.flags;
|
||||
memcpy(policy.master_key_descriptor, ctx.master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
if (copy_to_user(arg, &policy, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_policy);
|
||||
EXPORT_SYMBOL(fscrypt_ioctl_get_policy);
|
||||
|
||||
/**
|
||||
* fscrypt_has_permitted_context() - is a file's encryption policy permitted
|
||||
@@ -258,9 +230,9 @@ EXPORT_SYMBOL(fscrypt_has_permitted_context);
|
||||
* @parent: Parent inode from which the context is inherited.
|
||||
* @child: Child inode that inherits the context from @parent.
|
||||
* @fs_data: private data given by FS.
|
||||
* @preload: preload child i_crypt_info
|
||||
* @preload: preload child i_crypt_info if true
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||
void *fs_data, bool preload)
|
||||
@@ -269,9 +241,6 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||
struct fscrypt_info *ci;
|
||||
int res;
|
||||
|
||||
if (!parent->i_sb->s_cop->set_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = fscrypt_get_encryption_info(parent);
|
||||
if (res < 0)
|
||||
return res;
|
||||
@@ -281,19 +250,11 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||
return -ENOKEY;
|
||||
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
if (fscrypt_dummy_context_enabled(parent)) {
|
||||
ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE);
|
||||
res = 0;
|
||||
} else {
|
||||
ctx.contents_encryption_mode = ci->ci_data_mode;
|
||||
ctx.filenames_encryption_mode = ci->ci_filename_mode;
|
||||
ctx.flags = ci->ci_flags;
|
||||
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
}
|
||||
ctx.contents_encryption_mode = ci->ci_data_mode;
|
||||
ctx.filenames_encryption_mode = ci->ci_filename_mode;
|
||||
ctx.flags = ci->ci_flags;
|
||||
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
res = parent->i_sb->s_cop->set_context(child, &ctx,
|
||||
sizeof(ctx), fs_data);
|
||||
|
||||
@@ -117,7 +117,7 @@ ecryptfs_get_key_payload_data(struct key *key)
|
||||
|
||||
auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
|
||||
if (!auth_tok)
|
||||
return (struct ecryptfs_auth_tok *)user_key_payload(key)->data;
|
||||
return (struct ecryptfs_auth_tok *)user_key_payload_locked(key)->data;
|
||||
else
|
||||
return auth_tok;
|
||||
}
|
||||
|
||||
+11
-46
@@ -32,7 +32,11 @@
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
#include <linux/fscrypt_supp.h>
|
||||
#else
|
||||
#include <linux/fscrypt_notsupp.h>
|
||||
#endif
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/percpu-rwsem.h>
|
||||
#ifdef __KERNEL__
|
||||
@@ -1342,11 +1346,6 @@ struct ext4_super_block {
|
||||
/* Number of quota types we support */
|
||||
#define EXT4_MAXQUOTAS 3
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
#define EXT4_KEY_DESC_PREFIX "ext4:"
|
||||
#define EXT4_KEY_DESC_PREFIX_SIZE 5
|
||||
#endif
|
||||
|
||||
/*
|
||||
* fourth extended-fs super-block data in memory
|
||||
*/
|
||||
@@ -1516,12 +1515,6 @@ struct ext4_sb_info {
|
||||
|
||||
/* Barrier between changing inodes' journal flags and writepages ops. */
|
||||
struct percpu_rw_semaphore s_journal_flag_rwsem;
|
||||
|
||||
/* Encryption support */
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
u8 key_prefix[EXT4_KEY_DESC_PREFIX_SIZE];
|
||||
u8 key_prefix_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||
@@ -2277,11 +2270,6 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
|
||||
struct ext4_group_desc *gdp);
|
||||
ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
||||
|
||||
static inline int ext4_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
return ext4_has_feature_encrypt(sb);
|
||||
}
|
||||
|
||||
static inline bool ext4_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
@@ -2330,28 +2318,6 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
}
|
||||
static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
|
||||
|
||||
#define fscrypt_set_d_op(i)
|
||||
#define fscrypt_get_ctx fscrypt_notsupp_get_ctx
|
||||
#define fscrypt_release_ctx fscrypt_notsupp_release_ctx
|
||||
#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page
|
||||
#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page
|
||||
#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages
|
||||
#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page
|
||||
#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page
|
||||
#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range
|
||||
#define fscrypt_process_policy fscrypt_notsupp_process_policy
|
||||
#define fscrypt_get_policy fscrypt_notsupp_get_policy
|
||||
#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context
|
||||
#define fscrypt_inherit_context fscrypt_notsupp_inherit_context
|
||||
#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info
|
||||
#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info
|
||||
#define fscrypt_setup_filename fscrypt_notsupp_setup_filename
|
||||
#define fscrypt_free_filename fscrypt_notsupp_free_filename
|
||||
#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size
|
||||
#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer
|
||||
#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer
|
||||
#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr
|
||||
#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
|
||||
#endif
|
||||
|
||||
/* dir.c */
|
||||
@@ -2373,17 +2339,16 @@ extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
void *buf, int buf_size,
|
||||
struct ext4_filename *fname,
|
||||
struct ext4_dir_entry_2 **dest_de);
|
||||
int ext4_insert_dentry(struct inode *dir,
|
||||
struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname);
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname);
|
||||
static inline void ext4_update_dx_flag(struct inode *inode)
|
||||
{
|
||||
if (!ext4_has_feature_dir_index(inode->i_sb))
|
||||
ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
|
||||
}
|
||||
static unsigned char ext4_filetype_table[] = {
|
||||
static const unsigned char ext4_filetype_table[] = {
|
||||
DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
|
||||
};
|
||||
|
||||
@@ -3071,7 +3036,7 @@ extern int ext4_handle_dirty_dirent_node(handle_t *handle,
|
||||
struct inode *inode,
|
||||
struct buffer_head *bh);
|
||||
#define S_SHIFT 12
|
||||
static unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = {
|
||||
static const unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = {
|
||||
[S_IFREG >> S_SHIFT] = EXT4_FT_REG_FILE,
|
||||
[S_IFDIR >> S_SHIFT] = EXT4_FT_DIR,
|
||||
[S_IFCHR >> S_SHIFT] = EXT4_FT_CHRDEV,
|
||||
|
||||
+12
-8
@@ -771,7 +771,7 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return ERR_PTR(-EPERM);
|
||||
return ERR_PTR(-ENOKEY);
|
||||
if (!handle)
|
||||
nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb);
|
||||
encrypt = 1;
|
||||
@@ -1093,6 +1093,17 @@ got:
|
||||
if (err)
|
||||
goto fail_drop;
|
||||
|
||||
/*
|
||||
* Since the encryption xattr will always be unique, create it first so
|
||||
* that it's less likely to end up in an external xattr block and
|
||||
* prevent its deduplication.
|
||||
*/
|
||||
if (encrypt) {
|
||||
err = fscrypt_inherit_context(dir, inode, handle, true);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
}
|
||||
|
||||
err = ext4_init_acl(handle, inode, dir);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
@@ -1114,13 +1125,6 @@ got:
|
||||
ei->i_datasync_tid = handle->h_transaction->t_tid;
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
/* give pointer to avoid set_context with journal ops. */
|
||||
err = fscrypt_inherit_context(dir, inode, &encrypt, true);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
}
|
||||
|
||||
err = ext4_mark_inode_dirty(handle, inode);
|
||||
if (err) {
|
||||
ext4_std_error(sb, err);
|
||||
|
||||
+1
-1
@@ -1035,7 +1035,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
|
||||
err = ext4_journal_get_write_access(handle, iloc->bh);
|
||||
if (err)
|
||||
return err;
|
||||
ext4_insert_dentry(dir, inode, de, inline_size, fname);
|
||||
ext4_insert_dentry(inode, de, inline_size, fname);
|
||||
|
||||
ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
|
||||
|
||||
|
||||
+4
-2
@@ -1166,7 +1166,8 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
|
||||
if (unlikely(err))
|
||||
page_zero_new_buffers(page, from, to);
|
||||
else if (decrypt)
|
||||
err = fscrypt_decrypt_page(page);
|
||||
err = fscrypt_decrypt_page(page->mapping->host, page,
|
||||
PAGE_SIZE, 0, page->index);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
@@ -3824,7 +3825,8 @@ static int __ext4_block_zero_page_range(handle_t *handle,
|
||||
/* We expect the key to be set. */
|
||||
BUG_ON(!fscrypt_has_encryption_key(inode));
|
||||
BUG_ON(blocksize != PAGE_SIZE);
|
||||
WARN_ON_ONCE(fscrypt_decrypt_page(page));
|
||||
WARN_ON_ONCE(fscrypt_decrypt_page(page->mapping->host,
|
||||
page, PAGE_SIZE, 0, page->index));
|
||||
}
|
||||
}
|
||||
if (ext4_should_journal_data(inode)) {
|
||||
|
||||
+9
-28
@@ -191,6 +191,7 @@ journal_err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
static int uuid_is_zero(__u8 u[16])
|
||||
{
|
||||
int i;
|
||||
@@ -200,6 +201,7 @@ static int uuid_is_zero(__u8 u[16])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ext4_ioctl_setflags(struct inode *inode,
|
||||
unsigned int flags)
|
||||
@@ -770,28 +772,19 @@ resizefs_out:
|
||||
}
|
||||
case EXT4_IOC_PRECACHE_EXTENTS:
|
||||
return ext4_ext_precache(inode);
|
||||
case EXT4_IOC_SET_ENCRYPTION_POLICY: {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
struct fscrypt_policy policy;
|
||||
|
||||
case EXT4_IOC_SET_ENCRYPTION_POLICY:
|
||||
if (!ext4_has_feature_encrypt(sb))
|
||||
return -EOPNOTSUPP;
|
||||
return fscrypt_ioctl_set_policy(filp, (const void __user *)arg);
|
||||
|
||||
if (copy_from_user(&policy,
|
||||
(struct fscrypt_policy __user *)arg,
|
||||
sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return fscrypt_process_policy(filp, &policy);
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
case EXT4_IOC_GET_ENCRYPTION_PWSALT: {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int err, err2;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
handle_t *handle;
|
||||
|
||||
if (!ext4_sb_has_crypto(sb))
|
||||
if (!ext4_has_feature_encrypt(sb))
|
||||
return -EOPNOTSUPP;
|
||||
if (uuid_is_zero(sbi->s_es->s_encrypt_pw_salt)) {
|
||||
err = mnt_want_write_file(filp);
|
||||
@@ -821,30 +814,18 @@ resizefs_out:
|
||||
sbi->s_es->s_encrypt_pw_salt, 16))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case EXT4_IOC_GET_ENCRYPTION_POLICY: {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
struct fscrypt_policy policy;
|
||||
int err = 0;
|
||||
|
||||
if (!ext4_encrypted_inode(inode))
|
||||
return -ENOENT;
|
||||
err = fscrypt_get_policy(inode, &policy);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user((void __user *)arg, &policy, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
case EXT4_IOC_GET_ENCRYPTION_POLICY:
|
||||
return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
|
||||
|
||||
case EXT4_IOC_FSGETXATTR:
|
||||
{
|
||||
struct fsxattr fa;
|
||||
|
||||
memset(&fa, 0, sizeof(struct fsxattr));
|
||||
ext4_get_inode_flags(ei);
|
||||
fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
|
||||
|
||||
if (ext4_has_feature_project(inode->i_sb)) {
|
||||
|
||||
+40
-93
@@ -1237,37 +1237,24 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE! unlike strncmp, ext4_match returns 1 for success, 0 for failure.
|
||||
* Test whether a directory entry matches the filename being searched for.
|
||||
*
|
||||
* `len <= EXT4_NAME_LEN' is guaranteed by caller.
|
||||
* `de != NULL' is guaranteed by caller.
|
||||
* Return: %true if the directory entry matches, otherwise %false.
|
||||
*/
|
||||
static inline int ext4_match(struct ext4_filename *fname,
|
||||
struct ext4_dir_entry_2 *de)
|
||||
static inline bool ext4_match(const struct ext4_filename *fname,
|
||||
const struct ext4_dir_entry_2 *de)
|
||||
{
|
||||
const void *name = fname_name(fname);
|
||||
u32 len = fname_len(fname);
|
||||
struct fscrypt_name f;
|
||||
|
||||
if (!de->inode)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
f.usr_fname = fname->usr_fname;
|
||||
f.disk_name = fname->disk_name;
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
if (unlikely(!name)) {
|
||||
if (fname->usr_fname->name[0] == '_') {
|
||||
int ret;
|
||||
if (de->name_len <= 32)
|
||||
return 0;
|
||||
ret = memcmp(de->name + ((de->name_len - 17) & ~15),
|
||||
fname->crypto_buf.name + 8, 16);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
}
|
||||
name = fname->crypto_buf.name;
|
||||
len = fname->crypto_buf.len;
|
||||
}
|
||||
f.crypto_buf = fname->crypto_buf;
|
||||
#endif
|
||||
if (de->name_len != len)
|
||||
return 0;
|
||||
return (memcmp(de->name, name, len) == 0) ? 1 : 0;
|
||||
return fscrypt_match_name(&f, de->name, de->name_len);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1281,48 +1268,31 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
|
||||
struct ext4_dir_entry_2 * de;
|
||||
char * dlimit;
|
||||
int de_len;
|
||||
int res;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)search_buf;
|
||||
dlimit = search_buf + buf_size;
|
||||
while ((char *) de < dlimit) {
|
||||
/* this code is executed quadratically often */
|
||||
/* do minimal checking `by hand' */
|
||||
if ((char *) de + de->name_len <= dlimit) {
|
||||
res = ext4_match(fname, de);
|
||||
if (res < 0) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
if (res > 0) {
|
||||
/* found a match - just to be sure, do
|
||||
* a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
bh->b_data,
|
||||
bh->b_size, offset)) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
*res_dir = de;
|
||||
res = 1;
|
||||
goto return_result;
|
||||
}
|
||||
|
||||
if ((char *) de + de->name_len <= dlimit &&
|
||||
ext4_match(fname, de)) {
|
||||
/* found a match - just to be sure, do
|
||||
* a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
|
||||
bh->b_size, offset))
|
||||
return -1;
|
||||
*res_dir = de;
|
||||
return 1;
|
||||
}
|
||||
/* prevent looping on a bad block */
|
||||
de_len = ext4_rec_len_from_disk(de->rec_len,
|
||||
dir->i_sb->s_blocksize);
|
||||
if (de_len <= 0) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
if (de_len <= 0)
|
||||
return -1;
|
||||
offset += de_len;
|
||||
de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
|
||||
}
|
||||
|
||||
res = 0;
|
||||
return_result:
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
|
||||
@@ -1378,6 +1348,8 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
|
||||
return NULL;
|
||||
|
||||
retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
|
||||
if (retval == -ENOENT)
|
||||
return NULL;
|
||||
if (retval)
|
||||
return ERR_PTR(retval);
|
||||
|
||||
@@ -1614,16 +1586,9 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
||||
if (!IS_ERR(inode) && ext4_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
int nokey = ext4_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
if (nokey) {
|
||||
iput(inode);
|
||||
return ERR_PTR(-ENOKEY);
|
||||
}
|
||||
ext4_warning(inode->i_sb,
|
||||
"Inconsistent encryption contexts: %lu/%lu",
|
||||
(unsigned long) dir->i_ino,
|
||||
(unsigned long) inode->i_ino);
|
||||
dir->i_ino, inode->i_ino);
|
||||
iput(inode);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
@@ -1831,24 +1796,15 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
int nlen, rlen;
|
||||
unsigned int offset = 0;
|
||||
char *top;
|
||||
int res;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)buf;
|
||||
top = buf + buf_size - reclen;
|
||||
while ((char *) de <= top) {
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
buf, buf_size, offset)) {
|
||||
res = -EFSCORRUPTED;
|
||||
goto return_result;
|
||||
}
|
||||
/* Provide crypto context and crypto buffer to ext4 match */
|
||||
res = ext4_match(fname, de);
|
||||
if (res < 0)
|
||||
goto return_result;
|
||||
if (res > 0) {
|
||||
res = -EEXIST;
|
||||
goto return_result;
|
||||
}
|
||||
buf, buf_size, offset))
|
||||
return -EFSCORRUPTED;
|
||||
if (ext4_match(fname, de))
|
||||
return -EEXIST;
|
||||
nlen = EXT4_DIR_REC_LEN(de->name_len);
|
||||
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
|
||||
if ((de->inode ? rlen - nlen : rlen) >= reclen)
|
||||
@@ -1856,22 +1812,17 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
|
||||
offset += rlen;
|
||||
}
|
||||
|
||||
if ((char *) de > top)
|
||||
res = -ENOSPC;
|
||||
else {
|
||||
*dest_de = de;
|
||||
res = 0;
|
||||
}
|
||||
return_result:
|
||||
return res;
|
||||
return -ENOSPC;
|
||||
|
||||
*dest_de = de;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext4_insert_dentry(struct inode *dir,
|
||||
struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname)
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname)
|
||||
{
|
||||
|
||||
int nlen, rlen;
|
||||
@@ -1890,7 +1841,6 @@ int ext4_insert_dentry(struct inode *dir,
|
||||
ext4_set_de_type(inode->i_sb, de, inode->i_mode);
|
||||
de->name_len = fname_len(fname);
|
||||
memcpy(de->name, fname_name(fname), fname_len(fname));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1926,11 +1876,8 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* By now the buffer is marked for journaling. Due to crypto operations,
|
||||
* the following function call may fail */
|
||||
err = ext4_insert_dentry(dir, inode, de, blocksize, fname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* By now the buffer is marked for journaling */
|
||||
ext4_insert_dentry(inode, de, blocksize, fname);
|
||||
|
||||
/*
|
||||
* XXX shouldn't update any times until successful
|
||||
@@ -3090,7 +3037,7 @@ static int ext4_symlink(struct inode *dir,
|
||||
if (err)
|
||||
return err;
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return -EPERM;
|
||||
return -ENOKEY;
|
||||
disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
|
||||
sizeof(struct fscrypt_symlink_data));
|
||||
sd = kzalloc(disk_link.len, GFP_KERNEL);
|
||||
|
||||
+2
-2
@@ -24,7 +24,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/fscrypto.h>
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
#include "xattr.h"
|
||||
@@ -470,7 +469,8 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||
gfp_t gfp_flags = GFP_NOFS;
|
||||
|
||||
retry_encrypt:
|
||||
data_page = fscrypt_encrypt_page(inode, page, gfp_flags);
|
||||
data_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0,
|
||||
page->index, gfp_flags);
|
||||
if (IS_ERR(data_page)) {
|
||||
ret = PTR_ERR(data_page);
|
||||
if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
|
||||
|
||||
+38
-30
@@ -1102,51 +1102,65 @@ static int ext4_get_context(struct inode *inode, void *ctx, size_t len)
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx, len);
|
||||
}
|
||||
|
||||
static int ext4_key_prefix(struct inode *inode, u8 **key)
|
||||
{
|
||||
*key = EXT4_SB(inode->i_sb)->key_prefix;
|
||||
return EXT4_SB(inode->i_sb)->key_prefix_size;
|
||||
}
|
||||
|
||||
static int ext4_prepare_context(struct inode *inode)
|
||||
{
|
||||
return ext4_convert_inline_data(inode);
|
||||
}
|
||||
|
||||
static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
|
||||
void *fs_data)
|
||||
{
|
||||
handle_t *handle;
|
||||
int res, res2;
|
||||
handle_t *handle = fs_data;
|
||||
int res, res2, retries = 0;
|
||||
|
||||
/* fs_data is null when internally used. */
|
||||
if (fs_data) {
|
||||
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx,
|
||||
len, 0);
|
||||
res = ext4_convert_inline_data(inode);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
/*
|
||||
* If a journal handle was specified, then the encryption context is
|
||||
* being set on a new inode via inheritance and is part of a larger
|
||||
* transaction to create the inode. Otherwise the encryption context is
|
||||
* being set on an existing inode in its own transaction. Only in the
|
||||
* latter case should the "retry on ENOSPC" logic be used.
|
||||
*/
|
||||
|
||||
if (handle) {
|
||||
res = ext4_xattr_set_handle(handle, inode,
|
||||
EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
ctx, len, 0);
|
||||
if (!res) {
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
ext4_clear_inode_state(inode,
|
||||
EXT4_STATE_MAY_INLINE_DATA);
|
||||
/*
|
||||
* Update inode->i_flags - e.g. S_DAX may get disabled
|
||||
*/
|
||||
ext4_set_inode_flags(inode);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
res = dquot_initialize(inode);
|
||||
if (res)
|
||||
return res;
|
||||
retry:
|
||||
handle = ext4_journal_start(inode, EXT4_HT_MISC,
|
||||
ext4_jbd2_credits_xattr(inode));
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
|
||||
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx,
|
||||
len, 0);
|
||||
res = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
ctx, len, 0);
|
||||
if (!res) {
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
/* Update inode->i_flags - e.g. S_DAX may get disabled */
|
||||
ext4_set_inode_flags(inode);
|
||||
res = ext4_mark_inode_dirty(handle, inode);
|
||||
if (res)
|
||||
EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
|
||||
}
|
||||
res2 = ext4_journal_stop(handle);
|
||||
|
||||
if (res == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||
goto retry;
|
||||
if (!res)
|
||||
res = res2;
|
||||
return res;
|
||||
@@ -1163,10 +1177,9 @@ static unsigned ext4_max_namelen(struct inode *inode)
|
||||
EXT4_NAME_LEN;
|
||||
}
|
||||
|
||||
static struct fscrypt_operations ext4_cryptops = {
|
||||
static const struct fscrypt_operations ext4_cryptops = {
|
||||
.key_prefix = "ext4:",
|
||||
.get_context = ext4_get_context,
|
||||
.key_prefix = ext4_key_prefix,
|
||||
.prepare_context = ext4_prepare_context,
|
||||
.set_context = ext4_set_context,
|
||||
.dummy_context = ext4_dummy_context,
|
||||
.is_encrypted = ext4_encrypted_inode,
|
||||
@@ -1174,7 +1187,7 @@ static struct fscrypt_operations ext4_cryptops = {
|
||||
.max_namelen = ext4_max_namelen,
|
||||
};
|
||||
#else
|
||||
static struct fscrypt_operations ext4_cryptops = {
|
||||
static const struct fscrypt_operations ext4_cryptops = {
|
||||
.is_encrypted = ext4_encrypted_inode,
|
||||
};
|
||||
#endif
|
||||
@@ -4220,11 +4233,6 @@ no_journal:
|
||||
ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);
|
||||
|
||||
kfree(orig_data);
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
memcpy(sbi->key_prefix, EXT4_KEY_DESC_PREFIX,
|
||||
EXT4_KEY_DESC_PREFIX_SIZE);
|
||||
sbi->key_prefix_size = EXT4_KEY_DESC_PREFIX_SIZE;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
cantfind_ext4:
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o
|
||||
|
||||
f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o
|
||||
f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o
|
||||
f2fs-y += shrinker.o extent_cache.o
|
||||
f2fs-y += shrinker.o extent_cache.o sysfs.o
|
||||
f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
|
||||
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
|
||||
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
||||
|
||||
+5
-4
@@ -207,15 +207,16 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
||||
void *value = NULL;
|
||||
size_t size = 0;
|
||||
int error;
|
||||
umode_t mode = inode->i_mode;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
if (acl && !ipage) {
|
||||
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||
error = posix_acl_update_mode(inode, &mode, &acl);
|
||||
if (error)
|
||||
return error;
|
||||
set_acl_inode(inode, inode->i_mode);
|
||||
set_acl_inode(inode, mode);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -233,7 +234,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
||||
value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);
|
||||
if (IS_ERR(value)) {
|
||||
clear_inode_flag(inode, FI_ACL_MODE);
|
||||
return (int)PTR_ERR(value);
|
||||
return PTR_ERR(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +385,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
|
||||
if (default_acl) {
|
||||
error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
|
||||
|
||||
+189
-79
@@ -31,7 +31,7 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io)
|
||||
set_ckpt_flags(sbi, CP_ERROR_FLAG);
|
||||
sbi->sb->s_flags |= MS_RDONLY;
|
||||
if (!end_io)
|
||||
f2fs_flush_merged_bios(sbi);
|
||||
f2fs_flush_merged_writes(sbi);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -65,7 +65,7 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index,
|
||||
.sbi = sbi,
|
||||
.type = META,
|
||||
.op = REQ_OP_READ,
|
||||
.op_flags = READ_SYNC | REQ_META | REQ_PRIO,
|
||||
.op_flags = REQ_META | REQ_PRIO,
|
||||
.old_blkaddr = index,
|
||||
.new_blkaddr = index,
|
||||
.encrypted_page = NULL,
|
||||
@@ -160,8 +160,9 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
||||
.sbi = sbi,
|
||||
.type = META,
|
||||
.op = REQ_OP_READ,
|
||||
.op_flags = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : REQ_RAHEAD,
|
||||
.op_flags = sync ? (REQ_META | REQ_PRIO) : REQ_RAHEAD,
|
||||
.encrypted_page = NULL,
|
||||
.in_list = false,
|
||||
};
|
||||
struct blk_plug plug;
|
||||
|
||||
@@ -207,12 +208,10 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
||||
}
|
||||
|
||||
fio.page = page;
|
||||
fio.old_blkaddr = fio.new_blkaddr;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
f2fs_submit_page_bio(&fio);
|
||||
f2fs_put_page(page, 0);
|
||||
}
|
||||
out:
|
||||
f2fs_submit_merged_bio(sbi, META, READ);
|
||||
blk_finish_plug(&plug);
|
||||
return blkno - start;
|
||||
}
|
||||
@@ -228,11 +227,12 @@ void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index)
|
||||
f2fs_put_page(page, 0);
|
||||
|
||||
if (readahead)
|
||||
ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR, true);
|
||||
ra_meta_pages(sbi, index, BIO_MAX_PAGES, META_POR, true);
|
||||
}
|
||||
|
||||
static int f2fs_write_meta_page(struct page *page,
|
||||
struct writeback_control *wbc)
|
||||
static int __f2fs_write_meta_page(struct page *page,
|
||||
struct writeback_control *wbc,
|
||||
enum iostat_type io_type)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
|
||||
@@ -245,16 +245,17 @@ static int f2fs_write_meta_page(struct page *page,
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto redirty_out;
|
||||
|
||||
write_meta_page(sbi, page);
|
||||
write_meta_page(sbi, page, io_type);
|
||||
dec_page_count(sbi, F2FS_DIRTY_META);
|
||||
|
||||
if (wbc->for_reclaim)
|
||||
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE);
|
||||
f2fs_submit_merged_write_cond(sbi, page->mapping->host,
|
||||
0, page->index, META);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
f2fs_submit_merged_bio(sbi, META, WRITE);
|
||||
f2fs_submit_merged_write(sbi, META);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -263,23 +264,33 @@ redirty_out:
|
||||
return AOP_WRITEPAGE_ACTIVATE;
|
||||
}
|
||||
|
||||
static int f2fs_write_meta_page(struct page *page,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
return __f2fs_write_meta_page(page, wbc, FS_META_IO);
|
||||
}
|
||||
|
||||
static int f2fs_write_meta_pages(struct address_space *mapping,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
|
||||
long diff, written;
|
||||
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
goto skip_write;
|
||||
|
||||
/* collect a number of dirty meta pages and write together */
|
||||
if (wbc->for_kupdate ||
|
||||
get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
|
||||
goto skip_write;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
/* if locked failed, cp will flush dirty pages instead */
|
||||
if (!mutex_trylock(&sbi->cp_mutex))
|
||||
goto skip_write;
|
||||
|
||||
/* if mounting is failed, skip writing node pages */
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
diff = nr_pages_to_write(sbi, META, wbc);
|
||||
written = sync_meta_pages(sbi, META, wbc->nr_to_write);
|
||||
written = sync_meta_pages(sbi, META, wbc->nr_to_write, FS_META_IO);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff);
|
||||
return 0;
|
||||
@@ -291,7 +302,7 @@ skip_write:
|
||||
}
|
||||
|
||||
long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
|
||||
long nr_to_write)
|
||||
long nr_to_write, enum iostat_type io_type)
|
||||
{
|
||||
struct address_space *mapping = META_MAPPING(sbi);
|
||||
pgoff_t index = 0, end = ULONG_MAX, prev = ULONG_MAX;
|
||||
@@ -342,7 +353,7 @@ continue_unlock:
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
goto continue_unlock;
|
||||
|
||||
if (mapping->a_ops->writepage(page, &wbc)) {
|
||||
if (__f2fs_write_meta_page(page, &wbc, io_type)) {
|
||||
unlock_page(page);
|
||||
break;
|
||||
}
|
||||
@@ -356,7 +367,7 @@ continue_unlock:
|
||||
}
|
||||
stop:
|
||||
if (nwritten)
|
||||
f2fs_submit_merged_bio(sbi, type, WRITE);
|
||||
f2fs_submit_merged_write(sbi, type);
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
@@ -493,6 +504,7 @@ int acquire_orphan_inode(struct f2fs_sb_info *sbi)
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(sbi, FAULT_ORPHAN)) {
|
||||
spin_unlock(&im->ino_lock);
|
||||
f2fs_show_injection_info(FAULT_ORPHAN);
|
||||
return -ENOSPC;
|
||||
}
|
||||
#endif
|
||||
@@ -565,7 +577,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
if (ni.blk_addr != NULL_ADDR) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"%s: orphan failed (ino=%x), run fsck to fix.",
|
||||
"%s: orphan failed (ino=%x) by kernel, retry mount.",
|
||||
__func__, ino);
|
||||
return -EIO;
|
||||
}
|
||||
@@ -576,11 +588,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
int recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
block_t start_blk, orphan_blocks, i, j;
|
||||
int err;
|
||||
unsigned int s_flags = sbi->sb->s_flags;
|
||||
int err = 0;
|
||||
|
||||
if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
|
||||
return 0;
|
||||
|
||||
if (s_flags & MS_RDONLY) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
|
||||
sbi->sb->s_flags &= ~MS_RDONLY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Needed for iput() to work correctly and not trash data */
|
||||
sbi->sb->s_flags |= MS_ACTIVE;
|
||||
/* Turn on quotas so that they are updated correctly */
|
||||
f2fs_enable_quota_files(sbi);
|
||||
#endif
|
||||
|
||||
start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
|
||||
orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
|
||||
|
||||
@@ -596,14 +621,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
||||
err = recover_orphan_inode(sbi, ino);
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
return err;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
/* clear Orphan Flag */
|
||||
clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG);
|
||||
return 0;
|
||||
out:
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Turn quotas off */
|
||||
f2fs_quota_off_umount(sbi->sb);
|
||||
#endif
|
||||
sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
|
||||
@@ -675,14 +707,13 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,
|
||||
*cp_block = (struct f2fs_checkpoint *)page_address(*cp_page);
|
||||
|
||||
crc_offset = le32_to_cpu((*cp_block)->checksum_offset);
|
||||
if (crc_offset >= blk_size) {
|
||||
if (crc_offset > (blk_size - sizeof(__le32))) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"invalid crc_offset: %zu", crc_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crc = le32_to_cpu(*((__le32 *)((unsigned char *)*cp_block
|
||||
+ crc_offset)));
|
||||
crc = cur_cp_crc(*cp_block);
|
||||
if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value");
|
||||
return -EINVAL;
|
||||
@@ -770,7 +801,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
|
||||
|
||||
/* Sanity checking of checkpoint */
|
||||
if (sanity_check_ckpt(sbi))
|
||||
goto fail_no_cp;
|
||||
goto free_fail_no_cp;
|
||||
|
||||
if (cur_page == cp1)
|
||||
sbi->cur_cp_pack = 1;
|
||||
@@ -798,6 +829,9 @@ done:
|
||||
f2fs_put_page(cp2, 1);
|
||||
return 0;
|
||||
|
||||
free_fail_no_cp:
|
||||
f2fs_put_page(cp1, 1);
|
||||
f2fs_put_page(cp2, 1);
|
||||
fail_no_cp:
|
||||
kfree(sbi->ckpt);
|
||||
return -EINVAL;
|
||||
@@ -812,7 +846,9 @@ static void __add_dirty_inode(struct inode *inode, enum inode_type type)
|
||||
return;
|
||||
|
||||
set_inode_flag(inode, flag);
|
||||
list_add_tail(&F2FS_I(inode)->dirty_list, &sbi->inode_list[type]);
|
||||
if (!f2fs_is_volatile_file(inode))
|
||||
list_add_tail(&F2FS_I(inode)->dirty_list,
|
||||
&sbi->inode_list[type]);
|
||||
stat_inc_dirty_inode(sbi, type);
|
||||
}
|
||||
|
||||
@@ -870,6 +906,7 @@ int sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type)
|
||||
struct inode *inode;
|
||||
struct f2fs_inode_info *fi;
|
||||
bool is_dir = (type == DIR_INODE);
|
||||
unsigned long ino = 0;
|
||||
|
||||
trace_f2fs_sync_dirty_inodes_enter(sbi->sb, is_dir,
|
||||
get_pages(sbi, is_dir ?
|
||||
@@ -888,18 +925,34 @@ retry:
|
||||
F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA));
|
||||
return 0;
|
||||
}
|
||||
fi = list_entry(head->next, struct f2fs_inode_info, dirty_list);
|
||||
fi = list_first_entry(head, struct f2fs_inode_info, dirty_list);
|
||||
inode = igrab(&fi->vfs_inode);
|
||||
spin_unlock(&sbi->inode_lock[type]);
|
||||
if (inode) {
|
||||
unsigned long cur_ino = inode->i_ino;
|
||||
|
||||
if (is_dir)
|
||||
F2FS_I(inode)->cp_task = current;
|
||||
|
||||
filemap_fdatawrite(inode->i_mapping);
|
||||
|
||||
if (is_dir)
|
||||
F2FS_I(inode)->cp_task = NULL;
|
||||
|
||||
iput(inode);
|
||||
/* We need to give cpu to another writers. */
|
||||
if (ino == cur_ino) {
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
cond_resched();
|
||||
} else {
|
||||
ino = cur_ino;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We should submit bio, since it exists several
|
||||
* wribacking dentry pages in the freeing inode.
|
||||
*/
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
f2fs_submit_merged_write(sbi, DATA);
|
||||
cond_resched();
|
||||
}
|
||||
goto retry;
|
||||
@@ -921,18 +974,35 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi)
|
||||
spin_unlock(&sbi->inode_lock[DIRTY_META]);
|
||||
return 0;
|
||||
}
|
||||
fi = list_entry(head->next, struct f2fs_inode_info,
|
||||
fi = list_first_entry(head, struct f2fs_inode_info,
|
||||
gdirty_list);
|
||||
inode = igrab(&fi->vfs_inode);
|
||||
spin_unlock(&sbi->inode_lock[DIRTY_META]);
|
||||
if (inode) {
|
||||
update_inode_page(inode);
|
||||
sync_inode_metadata(inode, 0);
|
||||
|
||||
/* it's on eviction */
|
||||
if (is_inode_flag_set(inode, FI_DIRTY_INODE))
|
||||
update_inode_page(inode);
|
||||
iput(inode);
|
||||
}
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __prepare_cp_block(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
nid_t last_nid = nm_i->next_scan_nid;
|
||||
|
||||
next_free_nid(sbi, &last_nid);
|
||||
ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi));
|
||||
ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi));
|
||||
ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi));
|
||||
ckpt->next_free_nid = cpu_to_le32(last_nid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Freeze all the FS-operations for checkpoint.
|
||||
*/
|
||||
@@ -956,33 +1026,47 @@ retry_flush_dents:
|
||||
err = sync_dirty_inodes(sbi, DIR_INODE);
|
||||
if (err)
|
||||
goto out;
|
||||
goto retry_flush_dents;
|
||||
}
|
||||
|
||||
if (get_pages(sbi, F2FS_DIRTY_IMETA)) {
|
||||
f2fs_unlock_all(sbi);
|
||||
err = f2fs_sync_inode_meta(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
goto retry_flush_dents;
|
||||
}
|
||||
|
||||
/*
|
||||
* POR: we should ensure that there are no dirty node pages
|
||||
* until finishing nat/sit flush.
|
||||
* until finishing nat/sit flush. inode->i_blocks can be updated.
|
||||
*/
|
||||
down_write(&sbi->node_change);
|
||||
|
||||
if (get_pages(sbi, F2FS_DIRTY_IMETA)) {
|
||||
up_write(&sbi->node_change);
|
||||
f2fs_unlock_all(sbi);
|
||||
err = f2fs_sync_inode_meta(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
goto retry_flush_dents;
|
||||
}
|
||||
|
||||
retry_flush_nodes:
|
||||
down_write(&sbi->node_write);
|
||||
|
||||
if (get_pages(sbi, F2FS_DIRTY_NODES)) {
|
||||
up_write(&sbi->node_write);
|
||||
err = sync_node_pages(sbi, &wbc);
|
||||
err = sync_node_pages(sbi, &wbc, false, FS_CP_NODE_IO);
|
||||
if (err) {
|
||||
up_write(&sbi->node_change);
|
||||
f2fs_unlock_all(sbi);
|
||||
goto out;
|
||||
}
|
||||
cond_resched();
|
||||
goto retry_flush_nodes;
|
||||
}
|
||||
|
||||
/*
|
||||
* sbi->node_change is used only for AIO write_begin path which produces
|
||||
* dirty node blocks and some checkpoint values by block allocation.
|
||||
*/
|
||||
__prepare_cp_block(sbi);
|
||||
up_write(&sbi->node_change);
|
||||
out:
|
||||
blk_finish_plug(&plug);
|
||||
return err;
|
||||
@@ -991,8 +1075,6 @@ out:
|
||||
static void unblock_operations(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
up_write(&sbi->node_write);
|
||||
|
||||
build_free_nids(sbi);
|
||||
f2fs_unlock_all(sbi);
|
||||
}
|
||||
|
||||
@@ -1003,7 +1085,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi)
|
||||
for (;;) {
|
||||
prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE);
|
||||
|
||||
if (!atomic_read(&sbi->nr_wb_bios))
|
||||
if (!get_pages(sbi, F2FS_WB_CP_DATA))
|
||||
break;
|
||||
|
||||
io_schedule_timeout(5*HZ);
|
||||
@@ -1015,15 +1097,24 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
{
|
||||
unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num;
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&sbi->cp_lock);
|
||||
spin_lock_irqsave(&sbi->cp_lock, flags);
|
||||
|
||||
if (cpc->reason == CP_UMOUNT)
|
||||
if ((cpc->reason & CP_UMOUNT) &&
|
||||
le32_to_cpu(ckpt->cp_pack_total_block_count) >
|
||||
sbi->blocks_per_seg - NM_I(sbi)->nat_bits_blocks)
|
||||
disable_nat_bits(sbi, false);
|
||||
|
||||
if (cpc->reason & CP_TRIMMED)
|
||||
__set_ckpt_flags(ckpt, CP_TRIMMED_FLAG);
|
||||
|
||||
if (cpc->reason & CP_UMOUNT)
|
||||
__set_ckpt_flags(ckpt, CP_UMOUNT_FLAG);
|
||||
else
|
||||
__clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG);
|
||||
|
||||
if (cpc->reason == CP_FASTBOOT)
|
||||
if (cpc->reason & CP_FASTBOOT)
|
||||
__set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG);
|
||||
else
|
||||
__clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG);
|
||||
@@ -1039,15 +1130,14 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
/* set this flag to activate crc|cp_ver for recovery */
|
||||
__set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG);
|
||||
|
||||
spin_unlock(&sbi->cp_lock);
|
||||
spin_unlock_irqrestore(&sbi->cp_lock, flags);
|
||||
}
|
||||
|
||||
static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
{
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num;
|
||||
nid_t last_nid = nm_i->next_scan_nid;
|
||||
unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num, flags;
|
||||
block_t start_blk;
|
||||
unsigned int data_sum_blocks, orphan_blocks;
|
||||
__u32 crc32 = 0;
|
||||
@@ -1059,19 +1149,16 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
|
||||
/* Flush all the NAT/SIT pages */
|
||||
while (get_pages(sbi, F2FS_DIRTY_META)) {
|
||||
sync_meta_pages(sbi, META, LONG_MAX);
|
||||
sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
next_free_nid(sbi, &last_nid);
|
||||
|
||||
/*
|
||||
* modify checkpoint
|
||||
* version number is already updated
|
||||
*/
|
||||
ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi));
|
||||
ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi));
|
||||
ckpt->free_segment_count = cpu_to_le32(free_segments(sbi));
|
||||
for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) {
|
||||
ckpt->cur_node_segno[i] =
|
||||
@@ -1090,18 +1177,14 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
curseg_alloc_type(sbi, i + CURSEG_HOT_DATA);
|
||||
}
|
||||
|
||||
ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi));
|
||||
ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi));
|
||||
ckpt->next_free_nid = cpu_to_le32(last_nid);
|
||||
|
||||
/* 2 cp + n data seg summary + orphan inode blocks */
|
||||
data_sum_blocks = npages_for_summary_flush(sbi, false);
|
||||
spin_lock(&sbi->cp_lock);
|
||||
spin_lock_irqsave(&sbi->cp_lock, flags);
|
||||
if (data_sum_blocks < NR_CURSEG_DATA_TYPE)
|
||||
__set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG);
|
||||
else
|
||||
__clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG);
|
||||
spin_unlock(&sbi->cp_lock);
|
||||
spin_unlock_irqrestore(&sbi->cp_lock, flags);
|
||||
|
||||
orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num);
|
||||
ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks +
|
||||
@@ -1130,6 +1213,27 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
|
||||
start_blk = __start_cp_next_addr(sbi);
|
||||
|
||||
/* write nat bits */
|
||||
if (enabled_nat_bits(sbi, cpc)) {
|
||||
__u64 cp_ver = cur_cp_version(ckpt);
|
||||
block_t blk;
|
||||
|
||||
cp_ver |= ((__u64)crc32 << 32);
|
||||
*(__le64 *)nm_i->nat_bits = cpu_to_le64(cp_ver);
|
||||
|
||||
blk = start_blk + sbi->blocks_per_seg - nm_i->nat_bits_blocks;
|
||||
for (i = 0; i < nm_i->nat_bits_blocks; i++)
|
||||
update_meta_page(sbi, nm_i->nat_bits +
|
||||
(i << F2FS_BLKSIZE_BITS), blk + i);
|
||||
|
||||
/* Flush all the NAT BITS pages */
|
||||
while (get_pages(sbi, F2FS_DIRTY_META)) {
|
||||
sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* need to wait for end_io results */
|
||||
wait_on_all_pages_writeback(sbi);
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
@@ -1179,7 +1283,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
percpu_counter_set(&sbi->alloc_valid_block_count, 0);
|
||||
|
||||
/* Here, we only have one bio having CP pack */
|
||||
sync_meta_pages(sbi, META_FLUSH, LONG_MAX);
|
||||
sync_meta_pages(sbi, META_FLUSH, LONG_MAX, FS_CP_META_IO);
|
||||
|
||||
/* wait for previous submitted meta pages writeback */
|
||||
wait_on_all_pages_writeback(sbi);
|
||||
@@ -1189,7 +1293,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
clear_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
clear_sbi_flag(sbi, SBI_NEED_CP);
|
||||
__set_cp_next_pack(sbi);
|
||||
@@ -1219,8 +1322,8 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
|
||||
(cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC ||
|
||||
(cpc->reason == CP_DISCARD && !sbi->discard_blks)))
|
||||
((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) ||
|
||||
((cpc->reason & CP_DISCARD) && !sbi->discard_blks)))
|
||||
goto out;
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
err = -EIO;
|
||||
@@ -1239,18 +1342,23 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
|
||||
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");
|
||||
|
||||
f2fs_flush_merged_bios(sbi);
|
||||
f2fs_flush_merged_writes(sbi);
|
||||
|
||||
/* this is the case of multiple fstrims without any changes */
|
||||
if (cpc->reason == CP_DISCARD && !is_sbi_flag_set(sbi, SBI_IS_DIRTY)) {
|
||||
f2fs_bug_on(sbi, NM_I(sbi)->dirty_nat_cnt);
|
||||
f2fs_bug_on(sbi, SIT_I(sbi)->dirty_sentries);
|
||||
f2fs_bug_on(sbi, prefree_segments(sbi));
|
||||
flush_sit_entries(sbi, cpc);
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
f2fs_wait_all_discard_bio(sbi);
|
||||
unblock_operations(sbi);
|
||||
goto out;
|
||||
if (cpc->reason & CP_DISCARD) {
|
||||
if (!exist_trim_candidates(sbi, cpc)) {
|
||||
unblock_operations(sbi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (NM_I(sbi)->dirty_nat_cnt == 0 &&
|
||||
SIT_I(sbi)->dirty_sentries == 0 &&
|
||||
prefree_segments(sbi) == 0) {
|
||||
flush_sit_entries(sbi, cpc);
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
unblock_operations(sbi);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1262,18 +1370,20 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver);
|
||||
|
||||
/* write cached NAT/SIT entries to NAT/SIT area */
|
||||
flush_nat_entries(sbi);
|
||||
flush_nat_entries(sbi, cpc);
|
||||
flush_sit_entries(sbi, cpc);
|
||||
|
||||
/* unlock all the fs_lock[] in do_checkpoint() */
|
||||
err = do_checkpoint(sbi, cpc);
|
||||
|
||||
f2fs_wait_all_discard_bio(sbi);
|
||||
if (err)
|
||||
release_discard_addrs(sbi);
|
||||
else
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
|
||||
unblock_operations(sbi);
|
||||
stat_inc_cp_count(sbi->stat_info);
|
||||
|
||||
if (cpc->reason == CP_RECOVERY)
|
||||
if (cpc->reason & CP_RECOVERY)
|
||||
f2fs_msg(sbi->sb, KERN_NOTICE,
|
||||
"checkpoint: version = %llx", ckpt_ver);
|
||||
|
||||
|
||||
+617
-286
File diff suppressed because it is too large
Load Diff
+67
-15
@@ -50,7 +50,27 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||
si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
|
||||
si->ndirty_all = sbi->ndirty_inode[DIRTY_META];
|
||||
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
|
||||
si->wb_bios = atomic_read(&sbi->nr_wb_bios);
|
||||
si->aw_cnt = atomic_read(&sbi->aw_cnt);
|
||||
si->vw_cnt = atomic_read(&sbi->vw_cnt);
|
||||
si->max_aw_cnt = atomic_read(&sbi->max_aw_cnt);
|
||||
si->max_vw_cnt = atomic_read(&sbi->max_vw_cnt);
|
||||
si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA);
|
||||
si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA);
|
||||
if (SM_I(sbi) && SM_I(sbi)->fcc_info) {
|
||||
si->nr_flushed =
|
||||
atomic_read(&SM_I(sbi)->fcc_info->issued_flush);
|
||||
si->nr_flushing =
|
||||
atomic_read(&SM_I(sbi)->fcc_info->issing_flush);
|
||||
}
|
||||
if (SM_I(sbi) && SM_I(sbi)->dcc_info) {
|
||||
si->nr_discarded =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->issued_discard);
|
||||
si->nr_discarding =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->issing_discard);
|
||||
si->nr_discard_cmd =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt);
|
||||
si->undiscard_blks = SM_I(sbi)->dcc_info->undiscard_blks;
|
||||
}
|
||||
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
|
||||
si->rsvd_segs = reserved_segments(sbi);
|
||||
si->overp_segs = overprovision_segments(sbi);
|
||||
@@ -61,6 +81,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||
si->inline_xattr = atomic_read(&sbi->inline_xattr);
|
||||
si->inline_inode = atomic_read(&sbi->inline_inode);
|
||||
si->inline_dir = atomic_read(&sbi->inline_dir);
|
||||
si->append = sbi->im[APPEND_INO].ino_num;
|
||||
si->update = sbi->im[UPDATE_INO].ino_num;
|
||||
si->orphans = sbi->im[ORPHAN_INO].ino_num;
|
||||
si->utilization = utilization(sbi);
|
||||
|
||||
@@ -74,7 +96,9 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||
si->dirty_nats = NM_I(sbi)->dirty_nat_cnt;
|
||||
si->sits = MAIN_SEGS(sbi);
|
||||
si->dirty_sits = SIT_I(sbi)->dirty_sentries;
|
||||
si->fnids = NM_I(sbi)->fcnt;
|
||||
si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID_LIST];
|
||||
si->avail_nids = NM_I(sbi)->available_nids;
|
||||
si->alloc_nids = NM_I(sbi)->nid_cnt[ALLOC_NID_LIST];
|
||||
si->bg_gc = sbi->bg_gc;
|
||||
si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg)
|
||||
* 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg)
|
||||
@@ -87,8 +111,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||
for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_NODE; i++) {
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, i);
|
||||
si->curseg[i] = curseg->segno;
|
||||
si->cursec[i] = curseg->segno / sbi->segs_per_sec;
|
||||
si->curzone[i] = si->cursec[i] / sbi->secs_per_zone;
|
||||
si->cursec[i] = GET_SEC_FROM_SEG(sbi, curseg->segno);
|
||||
si->curzone[i] = GET_ZONE_FROM_SEC(sbi, si->cursec[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
@@ -112,10 +136,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
|
||||
|
||||
bimodal = 0;
|
||||
total_vblocks = 0;
|
||||
blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
|
||||
blks_per_sec = BLKS_PER_SEC(sbi);
|
||||
hblks_per_sec = blks_per_sec / 2;
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
|
||||
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
||||
vblocks = get_valid_blocks(sbi, segno, true);
|
||||
dist = abs(vblocks - hblks_per_sec);
|
||||
bimodal += dist * dist;
|
||||
|
||||
@@ -144,7 +168,11 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||
if (si->base_mem)
|
||||
goto get_cache;
|
||||
|
||||
si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize;
|
||||
/* build stat */
|
||||
si->base_mem = sizeof(struct f2fs_stat_info);
|
||||
|
||||
/* build superblock */
|
||||
si->base_mem += sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize;
|
||||
si->base_mem += 2 * sizeof(struct f2fs_inode_info);
|
||||
si->base_mem += sizeof(*sbi->ckpt);
|
||||
si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE;
|
||||
@@ -181,6 +209,10 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||
/* build nm */
|
||||
si->base_mem += sizeof(struct f2fs_nm_info);
|
||||
si->base_mem += __bitmap_size(sbi, NAT_BITMAP);
|
||||
si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS);
|
||||
si->base_mem += NM_I(sbi)->nat_blocks * NAT_ENTRY_BITMAP_SIZE;
|
||||
si->base_mem += NM_I(sbi)->nat_blocks / 8;
|
||||
si->base_mem += NM_I(sbi)->nat_blocks * sizeof(unsigned short);
|
||||
|
||||
get_cache:
|
||||
si->cache_mem = 0;
|
||||
@@ -190,11 +222,18 @@ get_cache:
|
||||
si->cache_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
/* build merge flush thread */
|
||||
if (SM_I(sbi)->cmd_control_info)
|
||||
if (SM_I(sbi)->fcc_info)
|
||||
si->cache_mem += sizeof(struct flush_cmd_control);
|
||||
if (SM_I(sbi)->dcc_info) {
|
||||
si->cache_mem += sizeof(struct discard_cmd_control);
|
||||
si->cache_mem += sizeof(struct discard_cmd) *
|
||||
atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt);
|
||||
}
|
||||
|
||||
/* free nids */
|
||||
si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid);
|
||||
si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] +
|
||||
NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]) *
|
||||
sizeof(struct free_nid);
|
||||
si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry);
|
||||
si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
|
||||
sizeof(struct nat_entry_set);
|
||||
@@ -250,8 +289,8 @@ static int stat_show(struct seq_file *s, void *v)
|
||||
si->inline_inode);
|
||||
seq_printf(s, " - Inline_dentry Inode: %u\n",
|
||||
si->inline_dir);
|
||||
seq_printf(s, " - Orphan Inode: %u\n",
|
||||
si->orphans);
|
||||
seq_printf(s, " - Orphan/Append/Update Inode: %u, %u, %u\n",
|
||||
si->orphans, si->append, si->update);
|
||||
seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n",
|
||||
si->main_area_segs, si->main_area_sections,
|
||||
si->main_area_zones);
|
||||
@@ -310,8 +349,16 @@ static int stat_show(struct seq_file *s, void *v)
|
||||
seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
|
||||
si->ext_tree, si->zombie_tree, si->ext_node);
|
||||
seq_puts(s, "\nBalancing F2FS Async:\n");
|
||||
seq_printf(s, " - inmem: %4d, wb_bios: %4d\n",
|
||||
si->inmem_pages, si->wb_bios);
|
||||
seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), "
|
||||
"Discard: (%4d %4d)) cmd: %4d undiscard:%4u\n",
|
||||
si->nr_wb_cp_data, si->nr_wb_data,
|
||||
si->nr_flushing, si->nr_flushed,
|
||||
si->nr_discarding, si->nr_discarded,
|
||||
si->nr_discard_cmd, si->undiscard_blks);
|
||||
seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), "
|
||||
"volatile IO: %4d (Max. %4d)\n",
|
||||
si->inmem_pages, si->aw_cnt, si->max_aw_cnt,
|
||||
si->vw_cnt, si->max_vw_cnt);
|
||||
seq_printf(s, " - nodes: %4d in %4d\n",
|
||||
si->ndirty_node, si->node_pages);
|
||||
seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n",
|
||||
@@ -324,8 +371,8 @@ static int stat_show(struct seq_file *s, void *v)
|
||||
si->ndirty_imeta);
|
||||
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
|
||||
si->dirty_nats, si->nats, si->dirty_sits, si->sits);
|
||||
seq_printf(s, " - free_nids: %9d\n",
|
||||
si->fnids);
|
||||
seq_printf(s, " - free_nids: %9d/%9d\n - alloc_nids: %9d\n",
|
||||
si->free_nids, si->avail_nids, si->alloc_nids);
|
||||
seq_puts(s, "\nDistribution of User Blocks:");
|
||||
seq_puts(s, " [ valid | invalid | free ]\n");
|
||||
seq_puts(s, " [");
|
||||
@@ -410,6 +457,11 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
||||
atomic_set(&sbi->inline_dir, 0);
|
||||
atomic_set(&sbi->inplace_count, 0);
|
||||
|
||||
atomic_set(&sbi->aw_cnt, 0);
|
||||
atomic_set(&sbi->vw_cnt, 0);
|
||||
atomic_set(&sbi->max_aw_cnt, 0);
|
||||
atomic_set(&sbi->max_vw_cnt, 0);
|
||||
|
||||
mutex_lock(&f2fs_stat_mutex);
|
||||
list_add_tail(&si->stat_list, &f2fs_stat_list);
|
||||
mutex_unlock(&f2fs_stat_mutex);
|
||||
|
||||
+51
-72
@@ -94,7 +94,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
|
||||
dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
make_dentry_ptr_block(NULL, &d, dentry_blk);
|
||||
de = find_target_dentry(fname, namehash, max_slots, &d);
|
||||
if (de)
|
||||
*res_page = dentry_page;
|
||||
@@ -111,8 +111,6 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
|
||||
struct f2fs_dir_entry *de;
|
||||
unsigned long bit_pos = 0;
|
||||
int max_len = 0;
|
||||
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str *name = &fname->disk_name;
|
||||
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
@@ -130,29 +128,11 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (de->hash_code != namehash)
|
||||
goto not_match;
|
||||
|
||||
de_name.name = d->filename[bit_pos];
|
||||
de_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
if (unlikely(!name->name)) {
|
||||
if (fname->usr_fname->name[0] == '_') {
|
||||
if (de_name.len > 32 &&
|
||||
!memcmp(de_name.name + ((de_name.len - 17) & ~15),
|
||||
fname->crypto_buf.name + 8, 16))
|
||||
goto found;
|
||||
goto not_match;
|
||||
}
|
||||
name->name = fname->crypto_buf.name;
|
||||
name->len = fname->crypto_buf.len;
|
||||
}
|
||||
#endif
|
||||
if (de_name.len == name->len &&
|
||||
!memcmp(de_name.name, name->name, name->len))
|
||||
if (de->hash_code == namehash &&
|
||||
fscrypt_match_name(fname, d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len)))
|
||||
goto found;
|
||||
not_match:
|
||||
|
||||
if (max_slots && max_len > *max_slots)
|
||||
*max_slots = max_len;
|
||||
max_len = 0;
|
||||
@@ -212,13 +192,9 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||
f2fs_put_page(dentry_page, 0);
|
||||
}
|
||||
|
||||
/* This is to increase the speed of f2fs_create */
|
||||
if (!de && room) {
|
||||
F2FS_I(dir)->task = current;
|
||||
if (F2FS_I(dir)->chash != namehash) {
|
||||
F2FS_I(dir)->chash = namehash;
|
||||
F2FS_I(dir)->clevel = level;
|
||||
}
|
||||
if (!de && room && F2FS_I(dir)->chash != namehash) {
|
||||
F2FS_I(dir)->chash = namehash;
|
||||
F2FS_I(dir)->clevel = level;
|
||||
}
|
||||
|
||||
return de;
|
||||
@@ -259,6 +235,9 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
|
||||
break;
|
||||
}
|
||||
out:
|
||||
/* This is to increase the speed of f2fs_create */
|
||||
if (!de)
|
||||
F2FS_I(dir)->task = current;
|
||||
return de;
|
||||
}
|
||||
|
||||
@@ -277,7 +256,10 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
||||
|
||||
err = fscrypt_setup_filename(dir, child, 1, &fname);
|
||||
if (err) {
|
||||
*res_page = ERR_PTR(err);
|
||||
if (err == -ENOENT)
|
||||
*res_page = NULL;
|
||||
else
|
||||
*res_page = ERR_PTR(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -322,7 +304,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
|
||||
set_page_dirty(page);
|
||||
|
||||
dir->i_mtime = dir->i_ctime = current_time(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir, false);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
@@ -339,24 +321,6 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
|
||||
set_page_dirty(ipage);
|
||||
}
|
||||
|
||||
int update_dent_inode(struct inode *inode, struct inode *to,
|
||||
const struct qstr *name)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (file_enc_name(to))
|
||||
return 0;
|
||||
|
||||
page = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
init_dent_inode(name, page);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void do_make_empty_dir(struct inode *inode, struct inode *parent,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
@@ -386,7 +350,7 @@ static int make_empty_dir(struct inode *inode,
|
||||
|
||||
dentry_blk = kmap_atomic(dentry_page);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
make_dentry_ptr_block(NULL, &d, dentry_blk);
|
||||
do_make_empty_dir(inode, parent, &d);
|
||||
|
||||
kunmap_atomic(dentry_blk);
|
||||
@@ -440,15 +404,19 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
|
||||
set_cold_node(inode, page);
|
||||
}
|
||||
|
||||
if (new_name)
|
||||
if (new_name) {
|
||||
init_dent_inode(new_name, page);
|
||||
if (f2fs_encrypted_inode(dir))
|
||||
file_set_enc_name(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* This file should be checkpointed during fsync.
|
||||
* We lost i_pino from now on.
|
||||
*/
|
||||
if (is_inode_flag_set(inode, FI_INC_LINK)) {
|
||||
file_lost_pino(inode);
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
file_lost_pino(inode);
|
||||
/*
|
||||
* If link the tmpfile to alias through linkat path,
|
||||
* we should remove this inode from orphan list.
|
||||
@@ -475,7 +443,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode,
|
||||
clear_inode_flag(inode, FI_NEW_INODE);
|
||||
}
|
||||
dir->i_mtime = dir->i_ctime = current_time(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir, false);
|
||||
|
||||
if (F2FS_I(dir)->i_current_depth != current_depth)
|
||||
f2fs_i_depth_write(dir, current_depth);
|
||||
@@ -554,8 +522,10 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
|
||||
|
||||
start:
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(F2FS_I_SB(dir), FAULT_DIR_DEPTH))
|
||||
if (time_to_inject(F2FS_I_SB(dir), FAULT_DIR_DEPTH)) {
|
||||
f2fs_show_injection_info(FAULT_DIR_DEPTH);
|
||||
return -ENOSPC;
|
||||
}
|
||||
#endif
|
||||
if (unlikely(current_depth == MAX_DIR_HASH_DEPTH))
|
||||
return -ENOSPC;
|
||||
@@ -599,11 +569,9 @@ add_dentry:
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
if (f2fs_encrypted_inode(dir))
|
||||
file_set_enc_name(inode);
|
||||
}
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
make_dentry_ptr_block(NULL, &d, dentry_blk);
|
||||
f2fs_update_dentry(ino, mode, &d, new_name, dentry_hash, bit_pos);
|
||||
|
||||
set_page_dirty(dentry_page);
|
||||
@@ -737,6 +705,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
unsigned int bit_pos;
|
||||
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
|
||||
struct address_space *mapping = page_mapping(page);
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
||||
@@ -750,7 +720,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
dentry_blk = page_address(page);
|
||||
bit_pos = dentry - dentry_blk->dentry;
|
||||
for (i = 0; i < slots; i++)
|
||||
clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
|
||||
__clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
|
||||
|
||||
/* Let's check and deallocate this dentry page */
|
||||
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
|
||||
@@ -760,17 +730,23 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
set_page_dirty(page);
|
||||
|
||||
dir->i_ctime = dir->i_mtime = current_time(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir, false);
|
||||
|
||||
if (inode)
|
||||
f2fs_drop_nlink(dir, inode);
|
||||
|
||||
if (bit_pos == NR_DENTRY_IN_BLOCK &&
|
||||
!truncate_hole(dir, page->index, page->index + 1)) {
|
||||
spin_lock_irqsave(&mapping->tree_lock, flags);
|
||||
radix_tree_tag_clear(&mapping->page_tree, page_index(page),
|
||||
PAGECACHE_TAG_DIRTY);
|
||||
spin_unlock_irqrestore(&mapping->tree_lock, flags);
|
||||
|
||||
clear_page_dirty_for_io(page);
|
||||
ClearPagePrivate(page);
|
||||
ClearPageUptodate(page);
|
||||
inode_dec_dirty_pages(dir);
|
||||
remove_dirty_inode(dir);
|
||||
}
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
@@ -813,7 +789,7 @@ bool f2fs_empty_dir(struct inode *dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
unsigned int start_pos, struct fscrypt_str *fstr)
|
||||
{
|
||||
unsigned char d_type = DT_UNKNOWN;
|
||||
@@ -848,7 +824,7 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
(u32)de->hash_code, 0,
|
||||
&de_name, fstr);
|
||||
if (err)
|
||||
return true;
|
||||
return err;
|
||||
|
||||
de_name = *fstr;
|
||||
fstr->len = save_len;
|
||||
@@ -856,12 +832,12 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
|
||||
if (!dir_emit(ctx, de_name.name, de_name.len,
|
||||
le32_to_cpu(de->ino), d_type))
|
||||
return true;
|
||||
return 1;
|
||||
|
||||
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
|
||||
ctx->pos = start_pos + bit_pos;
|
||||
}
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
||||
@@ -900,17 +876,21 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
||||
dentry_page = get_lock_data_page(inode, n, false);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
err = PTR_ERR(dentry_page);
|
||||
if (err == -ENOENT)
|
||||
if (err == -ENOENT) {
|
||||
err = 0;
|
||||
continue;
|
||||
else
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dentry_blk = kmap(dentry_page);
|
||||
|
||||
make_dentry_ptr(inode, &d, (void *)dentry_blk, 1);
|
||||
make_dentry_ptr_block(inode, &d, dentry_blk);
|
||||
|
||||
if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) {
|
||||
err = f2fs_fill_dentries(ctx, &d,
|
||||
n * NR_DENTRY_IN_BLOCK, &fstr);
|
||||
if (err) {
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
break;
|
||||
@@ -920,10 +900,9 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
fscrypt_fname_free_buffer(&fstr);
|
||||
return err;
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int f2fs_dir_open(struct inode *inode, struct file *filp)
|
||||
|
||||
+224
-155
@@ -18,6 +18,179 @@
|
||||
#include "node.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static struct rb_entry *__lookup_rb_tree_fast(struct rb_entry *cached_re,
|
||||
unsigned int ofs)
|
||||
{
|
||||
if (cached_re) {
|
||||
if (cached_re->ofs <= ofs &&
|
||||
cached_re->ofs + cached_re->len > ofs) {
|
||||
return cached_re;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct rb_entry *__lookup_rb_tree_slow(struct rb_root *root,
|
||||
unsigned int ofs)
|
||||
{
|
||||
struct rb_node *node = root->rb_node;
|
||||
struct rb_entry *re;
|
||||
|
||||
while (node) {
|
||||
re = rb_entry(node, struct rb_entry, rb_node);
|
||||
|
||||
if (ofs < re->ofs)
|
||||
node = node->rb_left;
|
||||
else if (ofs >= re->ofs + re->len)
|
||||
node = node->rb_right;
|
||||
else
|
||||
return re;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct rb_entry *__lookup_rb_tree(struct rb_root *root,
|
||||
struct rb_entry *cached_re, unsigned int ofs)
|
||||
{
|
||||
struct rb_entry *re;
|
||||
|
||||
re = __lookup_rb_tree_fast(cached_re, ofs);
|
||||
if (!re)
|
||||
return __lookup_rb_tree_slow(root, ofs);
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
struct rb_node **__lookup_rb_tree_for_insert(struct f2fs_sb_info *sbi,
|
||||
struct rb_root *root, struct rb_node **parent,
|
||||
unsigned int ofs)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_entry *re;
|
||||
|
||||
while (*p) {
|
||||
*parent = *p;
|
||||
re = rb_entry(*parent, struct rb_entry, rb_node);
|
||||
|
||||
if (ofs < re->ofs)
|
||||
p = &(*p)->rb_left;
|
||||
else if (ofs >= re->ofs + re->len)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup rb entry in position of @ofs in rb-tree,
|
||||
* if hit, return the entry, otherwise, return NULL
|
||||
* @prev_ex: extent before ofs
|
||||
* @next_ex: extent after ofs
|
||||
* @insert_p: insert point for new extent at ofs
|
||||
* in order to simpfy the insertion after.
|
||||
* tree must stay unchanged between lookup and insertion.
|
||||
*/
|
||||
struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root,
|
||||
struct rb_entry *cached_re,
|
||||
unsigned int ofs,
|
||||
struct rb_entry **prev_entry,
|
||||
struct rb_entry **next_entry,
|
||||
struct rb_node ***insert_p,
|
||||
struct rb_node **insert_parent,
|
||||
bool force)
|
||||
{
|
||||
struct rb_node **pnode = &root->rb_node;
|
||||
struct rb_node *parent = NULL, *tmp_node;
|
||||
struct rb_entry *re = cached_re;
|
||||
|
||||
*insert_p = NULL;
|
||||
*insert_parent = NULL;
|
||||
*prev_entry = NULL;
|
||||
*next_entry = NULL;
|
||||
|
||||
if (RB_EMPTY_ROOT(root))
|
||||
return NULL;
|
||||
|
||||
if (re) {
|
||||
if (re->ofs <= ofs && re->ofs + re->len > ofs)
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
while (*pnode) {
|
||||
parent = *pnode;
|
||||
re = rb_entry(*pnode, struct rb_entry, rb_node);
|
||||
|
||||
if (ofs < re->ofs)
|
||||
pnode = &(*pnode)->rb_left;
|
||||
else if (ofs >= re->ofs + re->len)
|
||||
pnode = &(*pnode)->rb_right;
|
||||
else
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
*insert_p = pnode;
|
||||
*insert_parent = parent;
|
||||
|
||||
re = rb_entry(parent, struct rb_entry, rb_node);
|
||||
tmp_node = parent;
|
||||
if (parent && ofs > re->ofs)
|
||||
tmp_node = rb_next(parent);
|
||||
*next_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node);
|
||||
|
||||
tmp_node = parent;
|
||||
if (parent && ofs < re->ofs)
|
||||
tmp_node = rb_prev(parent);
|
||||
*prev_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node);
|
||||
return NULL;
|
||||
|
||||
lookup_neighbors:
|
||||
if (ofs == re->ofs || force) {
|
||||
/* lookup prev node for merging backward later */
|
||||
tmp_node = rb_prev(&re->rb_node);
|
||||
*prev_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node);
|
||||
}
|
||||
if (ofs == re->ofs + re->len - 1 || force) {
|
||||
/* lookup next node for merging frontward later */
|
||||
tmp_node = rb_next(&re->rb_node);
|
||||
*next_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node);
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
bool __check_rb_tree_consistence(struct f2fs_sb_info *sbi,
|
||||
struct rb_root *root)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
struct rb_node *cur = rb_first(root), *next;
|
||||
struct rb_entry *cur_re, *next_re;
|
||||
|
||||
if (!cur)
|
||||
return true;
|
||||
|
||||
while (cur) {
|
||||
next = rb_next(cur);
|
||||
if (!next)
|
||||
return true;
|
||||
|
||||
cur_re = rb_entry(cur, struct rb_entry, rb_node);
|
||||
next_re = rb_entry(next, struct rb_entry, rb_node);
|
||||
|
||||
if (cur_re->ofs + cur_re->len > next_re->ofs) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO, "inconsistent rbtree, "
|
||||
"cur(%u, %u) next(%u, %u)",
|
||||
cur_re->ofs, cur_re->len,
|
||||
next_re->ofs, next_re->len);
|
||||
return false;
|
||||
}
|
||||
|
||||
cur = next;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct kmem_cache *extent_tree_slab;
|
||||
static struct kmem_cache *extent_node_slab;
|
||||
|
||||
@@ -77,7 +250,7 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode)
|
||||
struct extent_tree *et;
|
||||
nid_t ino = inode->i_ino;
|
||||
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
mutex_lock(&sbi->extent_tree_lock);
|
||||
et = radix_tree_lookup(&sbi->extent_tree_root, ino);
|
||||
if (!et) {
|
||||
et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS);
|
||||
@@ -94,7 +267,7 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode)
|
||||
atomic_dec(&sbi->total_zombie_tree);
|
||||
list_del_init(&et->list);
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
mutex_unlock(&sbi->extent_tree_lock);
|
||||
|
||||
/* never died until evict_inode */
|
||||
F2FS_I(inode)->extent_tree = et;
|
||||
@@ -102,36 +275,6 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode)
|
||||
return et;
|
||||
}
|
||||
|
||||
static struct extent_node *__lookup_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, unsigned int fofs)
|
||||
{
|
||||
struct rb_node *node = et->root.rb_node;
|
||||
struct extent_node *en = et->cached_en;
|
||||
|
||||
if (en) {
|
||||
struct extent_info *cei = &en->ei;
|
||||
|
||||
if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) {
|
||||
stat_inc_cached_node_hit(sbi);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
while (node) {
|
||||
en = rb_entry(node, struct extent_node, rb_node);
|
||||
|
||||
if (fofs < en->ei.fofs) {
|
||||
node = node->rb_left;
|
||||
} else if (fofs >= en->ei.fofs + en->ei.len) {
|
||||
node = node->rb_right;
|
||||
} else {
|
||||
stat_inc_rbtree_node_hit(sbi);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei)
|
||||
{
|
||||
@@ -172,12 +315,12 @@ static void __drop_largest_extent(struct inode *inode,
|
||||
|
||||
if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) {
|
||||
largest->len = 0;
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* return true, if inode page is changed */
|
||||
bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
|
||||
static bool __f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et;
|
||||
@@ -215,6 +358,16 @@ out:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
|
||||
{
|
||||
bool ret = __f2fs_init_extent_tree(inode, i_ext);
|
||||
|
||||
if (!F2FS_I(inode)->extent_tree)
|
||||
set_inode_flag(inode, FI_NO_EXTENT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
||||
struct extent_info *ei)
|
||||
{
|
||||
@@ -237,17 +390,24 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
en = __lookup_extent_tree(sbi, et, pgofs);
|
||||
if (en) {
|
||||
*ei = en->ei;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
ret = true;
|
||||
en = (struct extent_node *)__lookup_rb_tree(&et->root,
|
||||
(struct rb_entry *)et->cached_en, pgofs);
|
||||
if (!en)
|
||||
goto out;
|
||||
|
||||
if (en == et->cached_en)
|
||||
stat_inc_cached_node_hit(sbi);
|
||||
else
|
||||
stat_inc_rbtree_node_hit(sbi);
|
||||
|
||||
*ei = en->ei;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
ret = true;
|
||||
out:
|
||||
stat_inc_total_hit(sbi);
|
||||
read_unlock(&et->lock);
|
||||
@@ -256,87 +416,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lookup extent at @fofs, if hit, return the extent
|
||||
* if not, return NULL and
|
||||
* @prev_ex: extent before fofs
|
||||
* @next_ex: extent after fofs
|
||||
* @insert_p: insert point for new extent at fofs
|
||||
* in order to simpfy the insertion after.
|
||||
* tree must stay unchanged between lookup and insertion.
|
||||
*/
|
||||
static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et,
|
||||
unsigned int fofs,
|
||||
struct extent_node **prev_ex,
|
||||
struct extent_node **next_ex,
|
||||
struct rb_node ***insert_p,
|
||||
struct rb_node **insert_parent)
|
||||
{
|
||||
struct rb_node **pnode = &et->root.rb_node;
|
||||
struct rb_node *parent = NULL, *tmp_node;
|
||||
struct extent_node *en = et->cached_en;
|
||||
|
||||
*insert_p = NULL;
|
||||
*insert_parent = NULL;
|
||||
*prev_ex = NULL;
|
||||
*next_ex = NULL;
|
||||
|
||||
if (RB_EMPTY_ROOT(&et->root))
|
||||
return NULL;
|
||||
|
||||
if (en) {
|
||||
struct extent_info *cei = &en->ei;
|
||||
|
||||
if (cei->fofs <= fofs && cei->fofs + cei->len > fofs)
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
while (*pnode) {
|
||||
parent = *pnode;
|
||||
en = rb_entry(*pnode, struct extent_node, rb_node);
|
||||
|
||||
if (fofs < en->ei.fofs)
|
||||
pnode = &(*pnode)->rb_left;
|
||||
else if (fofs >= en->ei.fofs + en->ei.len)
|
||||
pnode = &(*pnode)->rb_right;
|
||||
else
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
*insert_p = pnode;
|
||||
*insert_parent = parent;
|
||||
|
||||
en = rb_entry(parent, struct extent_node, rb_node);
|
||||
tmp_node = parent;
|
||||
if (parent && fofs > en->ei.fofs)
|
||||
tmp_node = rb_next(parent);
|
||||
*next_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
|
||||
tmp_node = parent;
|
||||
if (parent && fofs < en->ei.fofs)
|
||||
tmp_node = rb_prev(parent);
|
||||
*prev_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
return NULL;
|
||||
|
||||
lookup_neighbors:
|
||||
if (fofs == en->ei.fofs) {
|
||||
/* lookup prev node for merging backward later */
|
||||
tmp_node = rb_prev(&en->rb_node);
|
||||
*prev_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
}
|
||||
if (fofs == en->ei.fofs + en->ei.len - 1) {
|
||||
/* lookup next node for merging frontward later */
|
||||
tmp_node = rb_next(&en->rb_node);
|
||||
*next_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
}
|
||||
return en;
|
||||
}
|
||||
|
||||
static struct extent_node *__try_merge_extent_node(struct inode *inode,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct extent_node *prev_ex,
|
||||
@@ -391,17 +470,7 @@ static struct extent_node *__insert_extent_tree(struct inode *inode,
|
||||
goto do_insert;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
en = rb_entry(parent, struct extent_node, rb_node);
|
||||
|
||||
if (ei->fofs < en->ei.fofs)
|
||||
p = &(*p)->rb_left;
|
||||
else if (ei->fofs >= en->ei.fofs + en->ei.len)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
p = __lookup_rb_tree_for_insert(sbi, &et->root, &parent, ei->fofs);
|
||||
do_insert:
|
||||
en = __attach_extent_node(sbi, et, ei, parent, p);
|
||||
if (!en)
|
||||
@@ -417,7 +486,7 @@ do_insert:
|
||||
return en;
|
||||
}
|
||||
|
||||
static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
static void f2fs_update_extent_tree_range(struct inode *inode,
|
||||
pgoff_t fofs, block_t blkaddr, unsigned int len)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
@@ -430,7 +499,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
unsigned int pos = (unsigned int)fofs;
|
||||
|
||||
if (!et)
|
||||
return false;
|
||||
return;
|
||||
|
||||
trace_f2fs_update_extent_tree_range(inode, fofs, blkaddr, len);
|
||||
|
||||
@@ -438,7 +507,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
|
||||
write_unlock(&et->lock);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
prev = et->largest;
|
||||
@@ -451,8 +520,11 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
__drop_largest_extent(inode, fofs, len);
|
||||
|
||||
/* 1. lookup first extent node in range [fofs, fofs + len - 1] */
|
||||
en = __lookup_extent_tree_ret(et, fofs, &prev_en, &next_en,
|
||||
&insert_p, &insert_parent);
|
||||
en = (struct extent_node *)__lookup_rb_tree_ret(&et->root,
|
||||
(struct rb_entry *)et->cached_en, fofs,
|
||||
(struct rb_entry **)&prev_en,
|
||||
(struct rb_entry **)&next_en,
|
||||
&insert_p, &insert_parent, false);
|
||||
if (!en)
|
||||
en = next_en;
|
||||
|
||||
@@ -493,9 +565,8 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
if (!next_en) {
|
||||
struct rb_node *node = rb_next(&en->rb_node);
|
||||
|
||||
next_en = node ?
|
||||
rb_entry(node, struct extent_node, rb_node)
|
||||
: NULL;
|
||||
next_en = rb_entry_safe(node, struct extent_node,
|
||||
rb_node);
|
||||
}
|
||||
|
||||
if (parts)
|
||||
@@ -536,8 +607,6 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
__free_extent_tree(sbi, et);
|
||||
|
||||
write_unlock(&et->lock);
|
||||
|
||||
return !__is_extent_same(&prev, &et->largest);
|
||||
}
|
||||
|
||||
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
@@ -553,7 +622,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
if (!atomic_read(&sbi->total_zombie_tree))
|
||||
goto free_node;
|
||||
|
||||
if (!down_write_trylock(&sbi->extent_tree_lock))
|
||||
if (!mutex_trylock(&sbi->extent_tree_lock))
|
||||
goto out;
|
||||
|
||||
/* 1. remove unreferenced extent tree */
|
||||
@@ -575,11 +644,11 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
goto unlock_out;
|
||||
cond_resched();
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
mutex_unlock(&sbi->extent_tree_lock);
|
||||
|
||||
free_node:
|
||||
/* 2. remove LRU extent entries */
|
||||
if (!down_write_trylock(&sbi->extent_tree_lock))
|
||||
if (!mutex_trylock(&sbi->extent_tree_lock))
|
||||
goto out;
|
||||
|
||||
remained = nr_shrink - (node_cnt + tree_cnt);
|
||||
@@ -609,7 +678,7 @@ free_node:
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
unlock_out:
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
mutex_unlock(&sbi->extent_tree_lock);
|
||||
out:
|
||||
trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
|
||||
|
||||
@@ -656,10 +725,10 @@ void f2fs_destroy_extent_tree(struct inode *inode)
|
||||
|
||||
if (inode->i_nlink && !is_bad_inode(inode) &&
|
||||
atomic_read(&et->node_cnt)) {
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
mutex_lock(&sbi->extent_tree_lock);
|
||||
list_add_tail(&et->list, &sbi->zombie_list);
|
||||
atomic_inc(&sbi->total_zombie_tree);
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
mutex_unlock(&sbi->extent_tree_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -667,12 +736,12 @@ void f2fs_destroy_extent_tree(struct inode *inode)
|
||||
node_cnt = f2fs_destroy_extent_node(inode);
|
||||
|
||||
/* delete extent tree entry in radix tree */
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
mutex_lock(&sbi->extent_tree_lock);
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
|
||||
kmem_cache_free(extent_tree_slab, et);
|
||||
atomic_dec(&sbi->total_ext_tree);
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
mutex_unlock(&sbi->extent_tree_lock);
|
||||
|
||||
F2FS_I(inode)->extent_tree = NULL;
|
||||
|
||||
@@ -719,7 +788,7 @@ void f2fs_update_extent_cache_range(struct dnode_of_data *dn,
|
||||
void init_extent_cache_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO);
|
||||
init_rwsem(&sbi->extent_tree_lock);
|
||||
mutex_init(&sbi->extent_tree_lock);
|
||||
INIT_LIST_HEAD(&sbi->extent_list);
|
||||
spin_lock_init(&sbi->extent_lock);
|
||||
atomic_set(&sbi->total_ext_tree, 0);
|
||||
|
||||
+1036
-460
File diff suppressed because it is too large
Load Diff
+385
-168
File diff suppressed because it is too large
Load Diff
+188
-82
@@ -28,17 +28,23 @@ static int gc_thread_func(void *data)
|
||||
struct f2fs_sb_info *sbi = data;
|
||||
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
||||
long wait_ms;
|
||||
unsigned int wait_ms;
|
||||
|
||||
wait_ms = gc_th->min_sleep_time;
|
||||
|
||||
set_freezable();
|
||||
do {
|
||||
wait_event_interruptible_timeout(*wq,
|
||||
kthread_should_stop() || freezing(current) ||
|
||||
gc_th->gc_wake,
|
||||
msecs_to_jiffies(wait_ms));
|
||||
|
||||
/* give it a try one time */
|
||||
if (gc_th->gc_wake)
|
||||
gc_th->gc_wake = 0;
|
||||
|
||||
if (try_to_freeze())
|
||||
continue;
|
||||
else
|
||||
wait_event_interruptible_timeout(*wq,
|
||||
kthread_should_stop(),
|
||||
msecs_to_jiffies(wait_ms));
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
|
||||
@@ -48,10 +54,15 @@ static int gc_thread_func(void *data)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(sbi, FAULT_CHECKPOINT))
|
||||
if (time_to_inject(sbi, FAULT_CHECKPOINT)) {
|
||||
f2fs_show_injection_info(FAULT_CHECKPOINT);
|
||||
f2fs_stop_checkpoint(sbi, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sb_start_write_trylock(sbi->sb))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* [GC triggering condition]
|
||||
* 0. GC is not conducted currently.
|
||||
@@ -66,23 +77,28 @@ static int gc_thread_func(void *data)
|
||||
* So, I'd like to wait some time to collect dirty segments.
|
||||
*/
|
||||
if (!mutex_trylock(&sbi->gc_mutex))
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
if (gc_th->gc_urgent) {
|
||||
wait_ms = gc_th->urgent_sleep_time;
|
||||
goto do_gc;
|
||||
}
|
||||
|
||||
if (!is_idle(sbi)) {
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (has_enough_invalid_blocks(sbi))
|
||||
decrease_sleep_time(gc_th, &wait_ms);
|
||||
else
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
|
||||
do_gc:
|
||||
stat_inc_bggc_count(sbi);
|
||||
|
||||
/* if return value is not zero, no victim was selected */
|
||||
if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC)))
|
||||
if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true, NULL_SEGNO))
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
|
||||
trace_f2fs_background_gc(sbi->sb, wait_ms,
|
||||
@@ -90,6 +106,8 @@ static int gc_thread_func(void *data)
|
||||
|
||||
/* balancing f2fs's metadata periodically */
|
||||
f2fs_balance_fs_bg(sbi);
|
||||
next:
|
||||
sb_end_write(sbi->sb);
|
||||
|
||||
} while (!kthread_should_stop());
|
||||
return 0;
|
||||
@@ -107,11 +125,14 @@ int start_gc_thread(struct f2fs_sb_info *sbi)
|
||||
goto out;
|
||||
}
|
||||
|
||||
gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME;
|
||||
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
|
||||
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
|
||||
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
|
||||
|
||||
gc_th->gc_idle = 0;
|
||||
gc_th->gc_urgent = 0;
|
||||
gc_th->gc_wake= 0;
|
||||
|
||||
sbi->gc_thread = gc_th;
|
||||
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
||||
@@ -170,7 +191,11 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
|
||||
if (gc_type != FG_GC && p->max_search > sbi->max_victim_search)
|
||||
p->max_search = sbi->max_victim_search;
|
||||
|
||||
p->offset = sbi->last_victim[p->gc_mode];
|
||||
/* let's select beginning hot/small space first */
|
||||
if (type == CURSEG_HOT_DATA || IS_NODESEG(type))
|
||||
p->offset = 0;
|
||||
else
|
||||
p->offset = SIT_I(sbi)->last_victim[p->gc_mode];
|
||||
}
|
||||
|
||||
static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
|
||||
@@ -180,7 +205,7 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
|
||||
if (p->alloc_mode == SSR)
|
||||
return sbi->blocks_per_seg;
|
||||
if (p->gc_mode == GC_GREEDY)
|
||||
return sbi->blocks_per_seg * p->ofs_unit;
|
||||
return 2 * sbi->blocks_per_seg * p->ofs_unit;
|
||||
else if (p->gc_mode == GC_CB)
|
||||
return UINT_MAX;
|
||||
else /* No other gc_mode */
|
||||
@@ -205,7 +230,7 @@ static unsigned int check_bg_victims(struct f2fs_sb_info *sbi)
|
||||
continue;
|
||||
|
||||
clear_bit(secno, dirty_i->victim_secmap);
|
||||
return secno * sbi->segs_per_sec;
|
||||
return GET_SEG_FROM_SEC(sbi, secno);
|
||||
}
|
||||
return NULL_SEGNO;
|
||||
}
|
||||
@@ -213,8 +238,8 @@ static unsigned int check_bg_victims(struct f2fs_sb_info *sbi)
|
||||
static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
unsigned int secno = GET_SECNO(sbi, segno);
|
||||
unsigned int start = secno * sbi->segs_per_sec;
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
unsigned int start = GET_SEG_FROM_SEC(sbi, secno);
|
||||
unsigned long long mtime = 0;
|
||||
unsigned int vblocks;
|
||||
unsigned char age = 0;
|
||||
@@ -223,7 +248,7 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
|
||||
for (i = 0; i < sbi->segs_per_sec; i++)
|
||||
mtime += get_seg_entry(sbi, start + i)->mtime;
|
||||
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
||||
vblocks = get_valid_blocks(sbi, segno, true);
|
||||
|
||||
mtime = div_u64(mtime, sbi->segs_per_sec);
|
||||
vblocks = div_u64(vblocks, sbi->segs_per_sec);
|
||||
@@ -242,6 +267,16 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
return UINT_MAX - ((100 * (100 - u) * age) / (100 + u));
|
||||
}
|
||||
|
||||
static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
{
|
||||
unsigned int valid_blocks =
|
||||
get_valid_blocks(sbi, segno, true);
|
||||
|
||||
return IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
||||
valid_blocks * 2 : valid_blocks;
|
||||
}
|
||||
|
||||
static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno, struct victim_sel_policy *p)
|
||||
{
|
||||
@@ -250,7 +285,7 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
|
||||
|
||||
/* alloc_mode == LFS */
|
||||
if (p->gc_mode == GC_GREEDY)
|
||||
return get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
||||
return get_greedy_cost(sbi, segno);
|
||||
else
|
||||
return get_cb_cost(sbi, segno);
|
||||
}
|
||||
@@ -279,6 +314,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
unsigned int *result, int gc_type, int type, char alloc_mode)
|
||||
{
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
struct sit_info *sm = SIT_I(sbi);
|
||||
struct victim_sel_policy p;
|
||||
unsigned int secno, last_victim;
|
||||
unsigned int last_segment = MAIN_SEGS(sbi);
|
||||
@@ -292,10 +328,18 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
p.min_segno = NULL_SEGNO;
|
||||
p.min_cost = get_max_cost(sbi, &p);
|
||||
|
||||
if (*result != NULL_SEGNO) {
|
||||
if (IS_DATASEG(get_seg_entry(sbi, *result)->type) &&
|
||||
get_valid_blocks(sbi, *result, false) &&
|
||||
!sec_usage_check(sbi, GET_SEC_FROM_SEG(sbi, *result)))
|
||||
p.min_segno = *result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p.max_search == 0)
|
||||
goto out;
|
||||
|
||||
last_victim = sbi->last_victim[p.gc_mode];
|
||||
last_victim = sm->last_victim[p.gc_mode];
|
||||
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
||||
p.min_segno = check_bg_victims(sbi);
|
||||
if (p.min_segno != NULL_SEGNO)
|
||||
@@ -308,9 +352,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
|
||||
segno = find_next_bit(p.dirty_segmap, last_segment, p.offset);
|
||||
if (segno >= last_segment) {
|
||||
if (sbi->last_victim[p.gc_mode]) {
|
||||
last_segment = sbi->last_victim[p.gc_mode];
|
||||
sbi->last_victim[p.gc_mode] = 0;
|
||||
if (sm->last_victim[p.gc_mode]) {
|
||||
last_segment =
|
||||
sm->last_victim[p.gc_mode];
|
||||
sm->last_victim[p.gc_mode] = 0;
|
||||
p.offset = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -327,7 +372,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
nsearched++;
|
||||
}
|
||||
|
||||
secno = GET_SECNO(sbi, segno);
|
||||
secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
|
||||
if (sec_usage_check(sbi, secno))
|
||||
goto next;
|
||||
@@ -345,17 +390,18 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
next:
|
||||
if (nsearched >= p.max_search) {
|
||||
if (!sbi->last_victim[p.gc_mode] && segno <= last_victim)
|
||||
sbi->last_victim[p.gc_mode] = last_victim + 1;
|
||||
if (!sm->last_victim[p.gc_mode] && segno <= last_victim)
|
||||
sm->last_victim[p.gc_mode] = last_victim + 1;
|
||||
else
|
||||
sbi->last_victim[p.gc_mode] = segno + 1;
|
||||
sm->last_victim[p.gc_mode] = segno + 1;
|
||||
sm->last_victim[p.gc_mode] %= MAIN_SEGS(sbi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p.min_segno != NULL_SEGNO) {
|
||||
got_it:
|
||||
if (p.alloc_mode == LFS) {
|
||||
secno = GET_SECNO(sbi, p.min_segno);
|
||||
secno = GET_SEC_FROM_SEG(sbi, p.min_segno);
|
||||
if (gc_type == FG_GC)
|
||||
sbi->cur_victim_sec = secno;
|
||||
else
|
||||
@@ -538,12 +584,14 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
get_node_info(sbi, nid, dni);
|
||||
|
||||
if (sum->version != dni->version) {
|
||||
f2fs_put_page(node_page, 1);
|
||||
return false;
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"%s: valid data with mismatched node version.",
|
||||
__func__);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
}
|
||||
|
||||
*nofs = ofs_of_node(node_page);
|
||||
source_blkaddr = datablock_addr(node_page, ofs_in_node);
|
||||
source_blkaddr = datablock_addr(NULL, node_page, ofs_in_node);
|
||||
f2fs_put_page(node_page, 1);
|
||||
|
||||
if (source_blkaddr != blkaddr)
|
||||
@@ -551,14 +599,21 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
/*
|
||||
* Move data block via META_MAPPING while keeping locked data page.
|
||||
* This can be used to move blocks, aka LBAs, directly on disk.
|
||||
*/
|
||||
static void move_data_block(struct inode *inode, block_t bidx,
|
||||
unsigned int segno, int off)
|
||||
{
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.temp = COLD,
|
||||
.op = REQ_OP_READ,
|
||||
.op_flags = READ_SYNC,
|
||||
.op_flags = 0,
|
||||
.encrypted_page = NULL,
|
||||
.in_list = false,
|
||||
};
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
@@ -572,6 +627,12 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
if (!check_valid_map(F2FS_I_SB(inode), segno, off))
|
||||
goto out;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
goto out;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
|
||||
if (err)
|
||||
@@ -596,7 +657,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
||||
|
||||
allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
|
||||
&sum, CURSEG_COLD_DATA);
|
||||
&sum, CURSEG_COLD_DATA, NULL, false);
|
||||
|
||||
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
|
||||
FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
||||
@@ -632,9 +693,11 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||
|
||||
fio.op = REQ_OP_WRITE;
|
||||
fio.op_flags = WRITE_SYNC;
|
||||
fio.op_flags = REQ_SYNC;
|
||||
fio.new_blkaddr = newaddr;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
f2fs_submit_page_write(&fio);
|
||||
|
||||
f2fs_update_iostat(fio.sbi, FS_GC_DATA_IO, F2FS_BLKSIZE);
|
||||
|
||||
f2fs_update_data_blkaddr(&dn, newaddr);
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
@@ -652,7 +715,8 @@ out:
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
|
||||
unsigned int segno, int off)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
@@ -660,6 +724,12 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
if (IS_ERR(page))
|
||||
return;
|
||||
|
||||
if (!check_valid_map(F2FS_I_SB(inode), segno, off))
|
||||
goto out;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
goto out;
|
||||
|
||||
if (gc_type == BG_GC) {
|
||||
if (PageWriteback(page))
|
||||
goto out;
|
||||
@@ -669,10 +739,14 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.temp = COLD,
|
||||
.op = REQ_OP_WRITE,
|
||||
.op_flags = WRITE_SYNC,
|
||||
.op_flags = REQ_SYNC,
|
||||
.old_blkaddr = NULL_ADDR,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
.need_lock = LOCK_REQ,
|
||||
.io_type = FS_GC_DATA_IO,
|
||||
};
|
||||
bool is_dirty = PageDirty(page);
|
||||
int err;
|
||||
@@ -680,8 +754,10 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
retry:
|
||||
set_page_dirty(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (clear_page_dirty_for_io(page))
|
||||
if (clear_page_dirty_for_io(page)) {
|
||||
inode_dec_dirty_pages(inode);
|
||||
remove_dirty_inode(inode);
|
||||
}
|
||||
|
||||
set_cold_data(page);
|
||||
|
||||
@@ -690,8 +766,6 @@ retry:
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
clear_cold_data(page);
|
||||
}
|
||||
out:
|
||||
f2fs_put_page(page, 1);
|
||||
@@ -761,8 +835,7 @@ next_step:
|
||||
continue;
|
||||
|
||||
/* if encrypted inode, let's go phase 3 */
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
if (f2fs_encrypted_file(inode)) {
|
||||
add_gc_inode(gc_list, inode);
|
||||
continue;
|
||||
}
|
||||
@@ -796,14 +869,18 @@ next_step:
|
||||
continue;
|
||||
}
|
||||
locked = true;
|
||||
|
||||
/* wait for all inflight aio data */
|
||||
inode_dio_wait(inode);
|
||||
}
|
||||
|
||||
start_bidx = start_bidx_of_node(nofs, inode)
|
||||
+ ofs_in_node;
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
move_encrypted_block(inode, start_bidx);
|
||||
if (f2fs_encrypted_file(inode))
|
||||
move_data_block(inode, start_bidx, segno, off);
|
||||
else
|
||||
move_data_page(inode, start_bidx, gc_type);
|
||||
move_data_page(inode, start_bidx, gc_type,
|
||||
segno, off);
|
||||
|
||||
if (locked) {
|
||||
up_write(&fi->dio_rwsem[WRITE]);
|
||||
@@ -840,7 +917,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
struct blk_plug plug;
|
||||
unsigned int segno = start_segno;
|
||||
unsigned int end_segno = start_segno + sbi->segs_per_sec;
|
||||
int sec_freed = 0;
|
||||
int seg_freed = 0;
|
||||
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
||||
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
||||
|
||||
@@ -864,7 +941,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
GET_SUM_BLOCK(sbi, segno));
|
||||
f2fs_put_page(sum_page, 0);
|
||||
|
||||
if (get_valid_blocks(sbi, segno, 1) == 0 ||
|
||||
if (get_valid_blocks(sbi, segno, false) == 0 ||
|
||||
!PageUptodate(sum_page) ||
|
||||
unlikely(f2fs_cp_error(sbi)))
|
||||
goto next;
|
||||
@@ -879,7 +956,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
* - mutex_lock(sentry_lock) - change_curseg()
|
||||
* - lock_page(sum_page)
|
||||
*/
|
||||
|
||||
if (type == SUM_TYPE_NODE)
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
else
|
||||
@@ -887,87 +963,113 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
gc_type);
|
||||
|
||||
stat_inc_seg_count(sbi, type, gc_type);
|
||||
|
||||
if (gc_type == FG_GC &&
|
||||
get_valid_blocks(sbi, segno, false) == 0)
|
||||
seg_freed++;
|
||||
next:
|
||||
f2fs_put_page(sum_page, 0);
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
f2fs_submit_merged_bio(sbi,
|
||||
(type == SUM_TYPE_NODE) ? NODE : DATA, WRITE);
|
||||
f2fs_submit_merged_write(sbi,
|
||||
(type == SUM_TYPE_NODE) ? NODE : DATA);
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
if (gc_type == FG_GC &&
|
||||
get_valid_blocks(sbi, start_segno, sbi->segs_per_sec) == 0)
|
||||
sec_freed = 1;
|
||||
|
||||
stat_inc_call_count(sbi->stat_info);
|
||||
|
||||
return sec_freed;
|
||||
return seg_freed;
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
|
||||
bool background, unsigned int segno)
|
||||
{
|
||||
unsigned int segno;
|
||||
int gc_type = sync ? FG_GC : BG_GC;
|
||||
int sec_freed = 0;
|
||||
int ret = -EINVAL;
|
||||
int sec_freed = 0, seg_freed = 0, total_freed = 0;
|
||||
int ret = 0;
|
||||
struct cp_control cpc;
|
||||
unsigned int init_segno = segno;
|
||||
struct gc_inode_list gc_list = {
|
||||
.ilist = LIST_HEAD_INIT(gc_list.ilist),
|
||||
.iroot = RADIX_TREE_INIT(GFP_NOFS),
|
||||
};
|
||||
|
||||
trace_f2fs_gc_begin(sbi->sb, sync, background,
|
||||
get_pages(sbi, F2FS_DIRTY_NODES),
|
||||
get_pages(sbi, F2FS_DIRTY_DENTS),
|
||||
get_pages(sbi, F2FS_DIRTY_IMETA),
|
||||
free_sections(sbi),
|
||||
free_segments(sbi),
|
||||
reserved_segments(sbi),
|
||||
prefree_segments(sbi));
|
||||
|
||||
cpc.reason = __get_cp_reason(sbi);
|
||||
gc_more:
|
||||
segno = NULL_SEGNO;
|
||||
|
||||
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
|
||||
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) {
|
||||
ret = -EINVAL;
|
||||
goto stop;
|
||||
}
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
ret = -EIO;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed, 0)) {
|
||||
gc_type = FG_GC;
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) {
|
||||
/*
|
||||
* If there is no victim and no prefree segment but still not
|
||||
* enough free sections, we should flush dent/node blocks and do
|
||||
* garbage collections.
|
||||
* For example, if there are many prefree_segments below given
|
||||
* threshold, we can make them free by checkpoint. Then, we
|
||||
* secure free segments which doesn't need fggc any more.
|
||||
*/
|
||||
if (__get_victim(sbi, &segno, gc_type) ||
|
||||
prefree_segments(sbi)) {
|
||||
ret = write_checkpoint(sbi, &cpc);
|
||||
if (ret)
|
||||
goto stop;
|
||||
segno = NULL_SEGNO;
|
||||
} else if (has_not_enough_free_secs(sbi, 0, 0)) {
|
||||
if (prefree_segments(sbi)) {
|
||||
ret = write_checkpoint(sbi, &cpc);
|
||||
if (ret)
|
||||
goto stop;
|
||||
}
|
||||
if (has_not_enough_free_secs(sbi, 0, 0))
|
||||
gc_type = FG_GC;
|
||||
}
|
||||
|
||||
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
|
||||
/* f2fs_balance_fs doesn't need to do BG_GC in critical path. */
|
||||
if (gc_type == BG_GC && !background) {
|
||||
ret = -EINVAL;
|
||||
goto stop;
|
||||
ret = 0;
|
||||
}
|
||||
if (!__get_victim(sbi, &segno, gc_type)) {
|
||||
ret = -ENODATA;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
if (do_garbage_collect(sbi, segno, &gc_list, gc_type) &&
|
||||
gc_type == FG_GC)
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
||||
if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
|
||||
sec_freed++;
|
||||
total_freed += seg_freed;
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
|
||||
if (!sync) {
|
||||
if (has_not_enough_free_secs(sbi, sec_freed, 0))
|
||||
if (has_not_enough_free_secs(sbi, sec_freed, 0)) {
|
||||
segno = NULL_SEGNO;
|
||||
goto gc_more;
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
ret = write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
stop:
|
||||
SIT_I(sbi)->last_victim[ALLOC_NEXT] = 0;
|
||||
SIT_I(sbi)->last_victim[FLUSH_DEVICE] = init_segno;
|
||||
|
||||
trace_f2fs_gc_end(sbi->sb, ret, total_freed, sec_freed,
|
||||
get_pages(sbi, F2FS_DIRTY_NODES),
|
||||
get_pages(sbi, F2FS_DIRTY_DENTS),
|
||||
get_pages(sbi, F2FS_DIRTY_IMETA),
|
||||
free_sections(sbi),
|
||||
free_segments(sbi),
|
||||
reserved_segments(sbi),
|
||||
prefree_segments(sbi));
|
||||
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
|
||||
put_gc_inode(&gc_list);
|
||||
@@ -979,7 +1081,7 @@ stop:
|
||||
|
||||
void build_gc_manager(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
u64 main_count, resv_count, ovp_count, blocks_per_sec;
|
||||
u64 main_count, resv_count, ovp_count;
|
||||
|
||||
DIRTY_I(sbi)->v_ops = &default_v_ops;
|
||||
|
||||
@@ -987,8 +1089,12 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
|
||||
main_count = SM_I(sbi)->main_segments << sbi->log_blocks_per_seg;
|
||||
resv_count = SM_I(sbi)->reserved_segments << sbi->log_blocks_per_seg;
|
||||
ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg;
|
||||
blocks_per_sec = sbi->blocks_per_seg * sbi->segs_per_sec;
|
||||
|
||||
sbi->fggc_threshold = div_u64((main_count - ovp_count) * blocks_per_sec,
|
||||
(main_count - resv_count));
|
||||
sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
|
||||
BLKS_PER_SEC(sbi), (main_count - resv_count));
|
||||
|
||||
/* give warm/cold data area from slower device */
|
||||
if (sbi->s_ndevs && sbi->segs_per_sec == 1)
|
||||
SIT_I(sbi)->last_victim[ALLOC_NEXT] =
|
||||
GET_SEGNO(sbi, FDEV(0).end_blk) + 1;
|
||||
}
|
||||
|
||||
+19
-8
@@ -13,6 +13,7 @@
|
||||
* whether IO subsystem is idle
|
||||
* or not
|
||||
*/
|
||||
#define DEF_GC_THREAD_URGENT_SLEEP_TIME 500 /* 500 ms */
|
||||
#define DEF_GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */
|
||||
#define DEF_GC_THREAD_MAX_SLEEP_TIME 60000
|
||||
#define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
|
||||
@@ -27,12 +28,15 @@ struct f2fs_gc_kthread {
|
||||
wait_queue_head_t gc_wait_queue_head;
|
||||
|
||||
/* for gc sleep time */
|
||||
unsigned int urgent_sleep_time;
|
||||
unsigned int min_sleep_time;
|
||||
unsigned int max_sleep_time;
|
||||
unsigned int no_gc_sleep_time;
|
||||
|
||||
/* for changing gc mode */
|
||||
unsigned int gc_idle;
|
||||
unsigned int gc_urgent;
|
||||
unsigned int gc_wake;
|
||||
};
|
||||
|
||||
struct gc_inode_list {
|
||||
@@ -65,25 +69,32 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
|
||||
}
|
||||
|
||||
static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
long *wait)
|
||||
unsigned int *wait)
|
||||
{
|
||||
unsigned int min_time = gc_th->min_sleep_time;
|
||||
unsigned int max_time = gc_th->max_sleep_time;
|
||||
|
||||
if (*wait == gc_th->no_gc_sleep_time)
|
||||
return;
|
||||
|
||||
*wait += gc_th->min_sleep_time;
|
||||
if (*wait > gc_th->max_sleep_time)
|
||||
*wait = gc_th->max_sleep_time;
|
||||
if ((long long)*wait + (long long)min_time > (long long)max_time)
|
||||
*wait = max_time;
|
||||
else
|
||||
*wait += min_time;
|
||||
}
|
||||
|
||||
static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
long *wait)
|
||||
unsigned int *wait)
|
||||
{
|
||||
unsigned int min_time = gc_th->min_sleep_time;
|
||||
|
||||
if (*wait == gc_th->no_gc_sleep_time)
|
||||
*wait = gc_th->max_sleep_time;
|
||||
|
||||
*wait -= gc_th->min_sleep_time;
|
||||
if (*wait <= gc_th->min_sleep_time)
|
||||
*wait = gc_th->min_sleep_time;
|
||||
if ((long long)*wait - (long long)min_time < (long long)min_time)
|
||||
*wait = min_time;
|
||||
else
|
||||
*wait -= min_time;
|
||||
}
|
||||
|
||||
static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
||||
|
||||
+102
-84
@@ -23,10 +23,10 @@ bool f2fs_may_inline_data(struct inode *inode)
|
||||
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))
|
||||
return false;
|
||||
|
||||
if (i_size_read(inode) > MAX_INLINE_DATA)
|
||||
if (i_size_read(inode) > MAX_INLINE_DATA(inode))
|
||||
return false;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
if (f2fs_encrypted_file(inode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -45,6 +45,7 @@ bool f2fs_may_inline_dentry(struct inode *inode)
|
||||
|
||||
void read_inline_data(struct page *page, struct page *ipage)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
void *src_addr, *dst_addr;
|
||||
|
||||
if (PageUptodate(page))
|
||||
@@ -52,31 +53,33 @@ void read_inline_data(struct page *page, struct page *ipage)
|
||||
|
||||
f2fs_bug_on(F2FS_P_SB(page), page->index);
|
||||
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE);
|
||||
zero_user_segment(page, MAX_INLINE_DATA(inode), PAGE_SIZE);
|
||||
|
||||
/* Copy the whole inline data block */
|
||||
src_addr = inline_data_addr(ipage);
|
||||
src_addr = inline_data_addr(inode, ipage);
|
||||
dst_addr = kmap_atomic(page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode));
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(dst_addr);
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
|
||||
bool truncate_inline_inode(struct page *ipage, u64 from)
|
||||
void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
if (from >= MAX_INLINE_DATA)
|
||||
return false;
|
||||
if (from >= MAX_INLINE_DATA(inode))
|
||||
return;
|
||||
|
||||
addr = inline_data_addr(ipage);
|
||||
addr = inline_data_addr(inode, ipage);
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
memset(addr + from, 0, MAX_INLINE_DATA - from);
|
||||
memset(addr + from, 0, MAX_INLINE_DATA(inode) - from);
|
||||
set_page_dirty(ipage);
|
||||
return true;
|
||||
|
||||
if (from == 0)
|
||||
clear_inode_flag(inode, FI_DATA_EXIST);
|
||||
}
|
||||
|
||||
int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
||||
@@ -129,9 +132,10 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
.sbi = F2FS_I_SB(dn->inode),
|
||||
.type = DATA,
|
||||
.op = REQ_OP_WRITE,
|
||||
.op_flags = WRITE_SYNC | REQ_PRIO,
|
||||
.op_flags = REQ_SYNC | REQ_PRIO,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
.io_type = FS_DATA_IO,
|
||||
};
|
||||
int dirty, err;
|
||||
|
||||
@@ -153,20 +157,23 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
/* write data page to try to make data consistent */
|
||||
set_page_writeback(page);
|
||||
fio.old_blkaddr = dn->data_blkaddr;
|
||||
set_inode_flag(dn->inode, FI_HOT_DATA);
|
||||
write_data_page(dn, &fio);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (dirty)
|
||||
if (dirty) {
|
||||
inode_dec_dirty_pages(dn->inode);
|
||||
remove_dirty_inode(dn->inode);
|
||||
}
|
||||
|
||||
/* this converted inline_data should be recovered. */
|
||||
set_inode_flag(dn->inode, FI_APPEND_WRITE);
|
||||
|
||||
/* clear inline data and flag after data writeback */
|
||||
truncate_inline_inode(dn->inode_page, 0);
|
||||
truncate_inline_inode(dn->inode, dn->inode_page, 0);
|
||||
clear_inline_node(dn->inode_page);
|
||||
clear_out:
|
||||
stat_dec_inline_inode(dn->inode);
|
||||
f2fs_clear_inline_inode(dn->inode);
|
||||
clear_inode_flag(dn->inode, FI_INLINE_DATA);
|
||||
f2fs_put_dnode(dn);
|
||||
return 0;
|
||||
}
|
||||
@@ -213,6 +220,8 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
{
|
||||
void *src_addr, *dst_addr;
|
||||
struct dnode_of_data dn;
|
||||
struct address_space *mapping = page_mapping(page);
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
@@ -229,11 +238,16 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true);
|
||||
src_addr = kmap_atomic(page);
|
||||
dst_addr = inline_data_addr(dn.inode_page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
dst_addr = inline_data_addr(inode, dn.inode_page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode));
|
||||
kunmap_atomic(src_addr);
|
||||
set_page_dirty(dn.inode_page);
|
||||
|
||||
spin_lock_irqsave(&mapping->tree_lock, flags);
|
||||
radix_tree_tag_clear(&mapping->page_tree, page_index(page),
|
||||
PAGECACHE_TAG_DIRTY);
|
||||
spin_unlock_irqrestore(&mapping->tree_lock, flags);
|
||||
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
|
||||
@@ -268,9 +282,9 @@ process_inline:
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
src_addr = inline_data_addr(npage);
|
||||
dst_addr = inline_data_addr(ipage);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
src_addr = inline_data_addr(inode, npage);
|
||||
dst_addr = inline_data_addr(inode, ipage);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode));
|
||||
|
||||
set_inode_flag(inode, FI_INLINE_DATA);
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
@@ -283,9 +297,8 @@ process_inline:
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
f2fs_bug_on(sbi, IS_ERR(ipage));
|
||||
if (!truncate_inline_inode(ipage, 0))
|
||||
return false;
|
||||
f2fs_clear_inline_inode(inode);
|
||||
truncate_inline_inode(inode, ipage, 0);
|
||||
clear_inode_flag(inode, FI_INLINE_DATA);
|
||||
f2fs_put_page(ipage, 1);
|
||||
} else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) {
|
||||
if (truncate_blocks(inode, 0, false))
|
||||
@@ -299,11 +312,11 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
struct fscrypt_name *fname, struct page **res_page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct page *ipage;
|
||||
void *inline_dentry;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
@@ -314,9 +327,9 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
|
||||
namehash = f2fs_dentry_hash(&name, fname);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
inline_dentry = inline_data_addr(dir, ipage);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
||||
make_dentry_ptr_inline(dir, &d, inline_dentry);
|
||||
de = find_target_dentry(fname, namehash, NULL, &d);
|
||||
unlock_page(ipage);
|
||||
if (de)
|
||||
@@ -330,19 +343,19 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
int make_empty_inline_dir(struct inode *inode, struct inode *parent,
|
||||
struct page *ipage)
|
||||
{
|
||||
struct f2fs_inline_dentry *dentry_blk;
|
||||
struct f2fs_dentry_ptr d;
|
||||
void *inline_dentry;
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
inline_dentry = inline_data_addr(inode, ipage);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
make_dentry_ptr_inline(inode, &d, inline_dentry);
|
||||
do_make_empty_dir(inode, parent, &d);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
|
||||
/* update i_size to MAX_INLINE_DATA */
|
||||
if (i_size_read(inode) < MAX_INLINE_DATA)
|
||||
f2fs_i_size_write(inode, MAX_INLINE_DATA);
|
||||
if (i_size_read(inode) < MAX_INLINE_DATA(inode))
|
||||
f2fs_i_size_write(inode, MAX_INLINE_DATA(inode));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -351,11 +364,12 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent,
|
||||
* release ipage in this function.
|
||||
*/
|
||||
static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
void *inline_dentry)
|
||||
{
|
||||
struct page *page;
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
struct f2fs_dentry_ptr src, dst;
|
||||
int err;
|
||||
|
||||
page = f2fs_grab_cache_page(dir->i_mapping, 0, false);
|
||||
@@ -370,25 +384,24 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
|
||||
goto out;
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE);
|
||||
zero_user_segment(page, MAX_INLINE_DATA(dir), PAGE_SIZE);
|
||||
|
||||
dentry_blk = kmap_atomic(page);
|
||||
|
||||
make_dentry_ptr_inline(dir, &src, inline_dentry);
|
||||
make_dentry_ptr_block(dir, &dst, dentry_blk);
|
||||
|
||||
/* copy data from inline dentry block to new dentry block */
|
||||
memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
|
||||
INLINE_DENTRY_BITMAP_SIZE);
|
||||
memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0,
|
||||
SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE);
|
||||
memcpy(dst.bitmap, src.bitmap, src.nr_bitmap);
|
||||
memset(dst.bitmap + src.nr_bitmap, 0, dst.nr_bitmap - src.nr_bitmap);
|
||||
/*
|
||||
* we do not need to zero out remainder part of dentry and filename
|
||||
* field, since we have used bitmap for marking the usage status of
|
||||
* them, besides, we can also ignore copying/zeroing reserved space
|
||||
* of dentry block, because them haven't been used so far.
|
||||
*/
|
||||
memcpy(dentry_blk->dentry, inline_dentry->dentry,
|
||||
sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY);
|
||||
memcpy(dentry_blk->filename, inline_dentry->filename,
|
||||
NR_INLINE_DENTRY * F2FS_SLOT_LEN);
|
||||
memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max);
|
||||
memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN);
|
||||
|
||||
kunmap_atomic(dentry_blk);
|
||||
if (!PageUptodate(page))
|
||||
@@ -396,7 +409,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
|
||||
set_page_dirty(page);
|
||||
|
||||
/* clear inline dir and flag after data writeback */
|
||||
truncate_inline_inode(ipage, 0);
|
||||
truncate_inline_inode(dir, ipage, 0);
|
||||
|
||||
stat_dec_inline_dir(dir);
|
||||
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
||||
@@ -409,14 +422,13 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_add_inline_entries(struct inode *dir,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry)
|
||||
{
|
||||
struct f2fs_dentry_ptr d;
|
||||
unsigned long bit_pos = 0;
|
||||
int err = 0;
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
||||
make_dentry_ptr_inline(dir, &d, inline_dentry);
|
||||
|
||||
while (bit_pos < d.max) {
|
||||
struct f2fs_dir_entry *de;
|
||||
@@ -437,7 +449,7 @@ static int f2fs_add_inline_entries(struct inode *dir,
|
||||
}
|
||||
|
||||
new_name.name = d.filename[bit_pos];
|
||||
new_name.len = de->name_len;
|
||||
new_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
ino = le32_to_cpu(de->ino);
|
||||
fake_mode = get_de_type(de) << S_SHIFT;
|
||||
@@ -458,20 +470,20 @@ punch_dentry_pages:
|
||||
}
|
||||
|
||||
static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
void *inline_dentry)
|
||||
{
|
||||
struct f2fs_inline_dentry *backup_dentry;
|
||||
void *backup_dentry;
|
||||
int err;
|
||||
|
||||
backup_dentry = f2fs_kmalloc(F2FS_I_SB(dir),
|
||||
sizeof(struct f2fs_inline_dentry), GFP_F2FS_ZERO);
|
||||
MAX_INLINE_DATA(dir), GFP_F2FS_ZERO);
|
||||
if (!backup_dentry) {
|
||||
f2fs_put_page(ipage, 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA);
|
||||
truncate_inline_inode(ipage, 0);
|
||||
memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA(dir));
|
||||
truncate_inline_inode(dir, ipage, 0);
|
||||
|
||||
unlock_page(ipage);
|
||||
|
||||
@@ -487,9 +499,9 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage,
|
||||
return 0;
|
||||
recover:
|
||||
lock_page(ipage);
|
||||
memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA);
|
||||
memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA(dir));
|
||||
f2fs_i_depth_write(dir, 0);
|
||||
f2fs_i_size_write(dir, MAX_INLINE_DATA);
|
||||
f2fs_i_size_write(dir, MAX_INLINE_DATA(dir));
|
||||
set_page_dirty(ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
||||
@@ -498,7 +510,7 @@ recover:
|
||||
}
|
||||
|
||||
static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
void *inline_dentry)
|
||||
{
|
||||
if (!F2FS_I(dir)->i_dir_level)
|
||||
return f2fs_move_inline_dirents(dir, ipage, inline_dentry);
|
||||
@@ -514,7 +526,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
|
||||
struct page *ipage;
|
||||
unsigned int bit_pos;
|
||||
f2fs_hash_t name_hash;
|
||||
struct f2fs_inline_dentry *dentry_blk = NULL;
|
||||
void *inline_dentry = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
int slots = GET_DENTRY_SLOTS(new_name->len);
|
||||
struct page *page = NULL;
|
||||
@@ -524,11 +536,12 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
|
||||
slots, NR_INLINE_DENTRY);
|
||||
if (bit_pos >= NR_INLINE_DENTRY) {
|
||||
err = f2fs_convert_inline_dir(dir, ipage, dentry_blk);
|
||||
inline_dentry = inline_data_addr(dir, ipage);
|
||||
make_dentry_ptr_inline(dir, &d, inline_dentry);
|
||||
|
||||
bit_pos = room_for_filename(d.bitmap, slots, d.max);
|
||||
if (bit_pos >= d.max) {
|
||||
err = f2fs_convert_inline_dir(dir, ipage, inline_dentry);
|
||||
if (err)
|
||||
return err;
|
||||
err = -EAGAIN;
|
||||
@@ -543,14 +556,11 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
if (f2fs_encrypted_inode(dir))
|
||||
file_set_enc_name(inode);
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
name_hash = f2fs_dentry_hash(new_name, NULL);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
@@ -573,7 +583,8 @@ out:
|
||||
void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
struct inode *dir, struct inode *inode)
|
||||
{
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
struct f2fs_dentry_ptr d;
|
||||
void *inline_dentry;
|
||||
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
|
||||
unsigned int bit_pos;
|
||||
int i;
|
||||
@@ -581,17 +592,18 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
|
||||
inline_dentry = inline_data_addr(page);
|
||||
bit_pos = dentry - inline_dentry->dentry;
|
||||
inline_dentry = inline_data_addr(dir, page);
|
||||
make_dentry_ptr_inline(dir, &d, inline_dentry);
|
||||
|
||||
bit_pos = dentry - d.dentry;
|
||||
for (i = 0; i < slots; i++)
|
||||
__clear_bit_le(bit_pos + i,
|
||||
&inline_dentry->dentry_bitmap);
|
||||
__clear_bit_le(bit_pos + i, d.bitmap);
|
||||
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
dir->i_ctime = dir->i_mtime = current_time(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir);
|
||||
f2fs_mark_inode_dirty_sync(dir, false);
|
||||
|
||||
if (inode)
|
||||
f2fs_drop_nlink(dir, inode);
|
||||
@@ -602,20 +614,21 @@ bool f2fs_empty_inline_dir(struct inode *dir)
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct page *ipage;
|
||||
unsigned int bit_pos = 2;
|
||||
struct f2fs_inline_dentry *dentry_blk;
|
||||
void *inline_dentry;
|
||||
struct f2fs_dentry_ptr d;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return false;
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
|
||||
NR_INLINE_DENTRY,
|
||||
bit_pos);
|
||||
inline_dentry = inline_data_addr(dir, ipage);
|
||||
make_dentry_ptr_inline(dir, &d, inline_dentry);
|
||||
|
||||
bit_pos = find_next_bit_le(d.bitmap, d.max, bit_pos);
|
||||
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
||||
if (bit_pos < NR_INLINE_DENTRY)
|
||||
if (bit_pos < d.max)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -625,26 +638,30 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
|
||||
struct fscrypt_str *fstr)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct f2fs_inline_dentry *inline_dentry = NULL;
|
||||
struct page *ipage = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
void *inline_dentry = NULL;
|
||||
int err;
|
||||
|
||||
if (ctx->pos == NR_INLINE_DENTRY)
|
||||
make_dentry_ptr_inline(inode, &d, inline_dentry);
|
||||
|
||||
if (ctx->pos == d.max)
|
||||
return 0;
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
inline_dentry = inline_data_addr(inode, ipage);
|
||||
|
||||
make_dentry_ptr(inode, &d, (void *)inline_dentry, 2);
|
||||
make_dentry_ptr_inline(inode, &d, inline_dentry);
|
||||
|
||||
if (!f2fs_fill_dentries(ctx, &d, 0, fstr))
|
||||
ctx->pos = NR_INLINE_DENTRY;
|
||||
err = f2fs_fill_dentries(ctx, &d, 0, fstr);
|
||||
if (!err)
|
||||
ctx->pos = d.max;
|
||||
|
||||
f2fs_put_page(ipage, 1);
|
||||
return 0;
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
int f2fs_inline_data_fiemap(struct inode *inode,
|
||||
@@ -666,7 +683,7 @@ int f2fs_inline_data_fiemap(struct inode *inode,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ilen = min_t(size_t, MAX_INLINE_DATA, i_size_read(inode));
|
||||
ilen = min_t(size_t, MAX_INLINE_DATA(inode), i_size_read(inode));
|
||||
if (start >= ilen)
|
||||
goto out;
|
||||
if (start + len < ilen)
|
||||
@@ -675,7 +692,8 @@ int f2fs_inline_data_fiemap(struct inode *inode,
|
||||
|
||||
get_node_info(F2FS_I_SB(inode), inode->i_ino, &ni);
|
||||
byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits;
|
||||
byteaddr += (char *)inline_data_addr(ipage) - (char *)F2FS_INODE(ipage);
|
||||
byteaddr += (char *)inline_data_addr(inode, ipage) -
|
||||
(char *)F2FS_INODE(ipage);
|
||||
err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags);
|
||||
out:
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
||||
+176
-35
@@ -16,13 +16,15 @@
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include "segment.h"
|
||||
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
void f2fs_mark_inode_dirty_sync(struct inode *inode)
|
||||
void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
|
||||
{
|
||||
if (f2fs_inode_dirtied(inode))
|
||||
if (f2fs_inode_dirtied(inode, sync))
|
||||
return;
|
||||
|
||||
mark_inode_dirty_sync(inode);
|
||||
}
|
||||
|
||||
@@ -43,25 +45,26 @@ void f2fs_set_inode_flags(struct inode *inode)
|
||||
new_fl |= S_DIRSYNC;
|
||||
inode_set_flags(inode, new_fl,
|
||||
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
}
|
||||
|
||||
static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
{
|
||||
int extra_size = get_extra_isize(inode);
|
||||
|
||||
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
if (ri->i_addr[0])
|
||||
inode->i_rdev =
|
||||
old_decode_dev(le32_to_cpu(ri->i_addr[0]));
|
||||
if (ri->i_addr[extra_size])
|
||||
inode->i_rdev = old_decode_dev(
|
||||
le32_to_cpu(ri->i_addr[extra_size]));
|
||||
else
|
||||
inode->i_rdev =
|
||||
new_decode_dev(le32_to_cpu(ri->i_addr[1]));
|
||||
inode->i_rdev = new_decode_dev(
|
||||
le32_to_cpu(ri->i_addr[extra_size + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
static bool __written_first_block(struct f2fs_inode *ri)
|
||||
{
|
||||
block_t addr = le32_to_cpu(ri->i_addr[0]);
|
||||
block_t addr = le32_to_cpu(ri->i_addr[offset_in_addr(ri)]);
|
||||
|
||||
if (addr != NEW_ADDR && addr != NULL_ADDR)
|
||||
return true;
|
||||
@@ -70,25 +73,27 @@ static bool __written_first_block(struct f2fs_inode *ri)
|
||||
|
||||
static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
{
|
||||
int extra_size = get_extra_isize(inode);
|
||||
|
||||
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
||||
if (old_valid_dev(inode->i_rdev)) {
|
||||
ri->i_addr[0] =
|
||||
ri->i_addr[extra_size] =
|
||||
cpu_to_le32(old_encode_dev(inode->i_rdev));
|
||||
ri->i_addr[1] = 0;
|
||||
ri->i_addr[extra_size + 1] = 0;
|
||||
} else {
|
||||
ri->i_addr[0] = 0;
|
||||
ri->i_addr[1] =
|
||||
ri->i_addr[extra_size] = 0;
|
||||
ri->i_addr[extra_size + 1] =
|
||||
cpu_to_le32(new_encode_dev(inode->i_rdev));
|
||||
ri->i_addr[2] = 0;
|
||||
ri->i_addr[extra_size + 2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __recover_inline_status(struct inode *inode, struct page *ipage)
|
||||
{
|
||||
void *inline_data = inline_data_addr(ipage);
|
||||
void *inline_data = inline_data_addr(inode, ipage);
|
||||
__le32 *start = inline_data;
|
||||
__le32 *end = start + MAX_INLINE_DATA / sizeof(__le32);
|
||||
__le32 *end = start + MAX_INLINE_DATA(inode) / sizeof(__le32);
|
||||
|
||||
while (start < end) {
|
||||
if (*start++) {
|
||||
@@ -103,12 +108,84 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage)
|
||||
return;
|
||||
}
|
||||
|
||||
static bool f2fs_enable_inode_chksum(struct f2fs_sb_info *sbi, struct page *page)
|
||||
{
|
||||
struct f2fs_inode *ri = &F2FS_NODE(page)->i;
|
||||
int extra_isize = le32_to_cpu(ri->i_extra_isize);
|
||||
|
||||
if (!f2fs_sb_has_inode_chksum(sbi->sb))
|
||||
return false;
|
||||
|
||||
if (!RAW_IS_INODE(F2FS_NODE(page)) || !(ri->i_inline & F2FS_EXTRA_ATTR))
|
||||
return false;
|
||||
|
||||
if (!F2FS_FITS_IN_INODE(ri, extra_isize, i_inode_checksum))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static __u32 f2fs_inode_chksum(struct f2fs_sb_info *sbi, struct page *page)
|
||||
{
|
||||
struct f2fs_node *node = F2FS_NODE(page);
|
||||
struct f2fs_inode *ri = &node->i;
|
||||
__le32 ino = node->footer.ino;
|
||||
__le32 gen = ri->i_generation;
|
||||
__u32 chksum, chksum_seed;
|
||||
__u32 dummy_cs = 0;
|
||||
unsigned int offset = offsetof(struct f2fs_inode, i_inode_checksum);
|
||||
unsigned int cs_size = sizeof(dummy_cs);
|
||||
|
||||
chksum = f2fs_chksum(sbi, sbi->s_chksum_seed, (__u8 *)&ino,
|
||||
sizeof(ino));
|
||||
chksum_seed = f2fs_chksum(sbi, chksum, (__u8 *)&gen, sizeof(gen));
|
||||
|
||||
chksum = f2fs_chksum(sbi, chksum_seed, (__u8 *)ri, offset);
|
||||
chksum = f2fs_chksum(sbi, chksum, (__u8 *)&dummy_cs, cs_size);
|
||||
offset += cs_size;
|
||||
chksum = f2fs_chksum(sbi, chksum, (__u8 *)ri + offset,
|
||||
F2FS_BLKSIZE - offset);
|
||||
return chksum;
|
||||
}
|
||||
|
||||
bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page)
|
||||
{
|
||||
struct f2fs_inode *ri;
|
||||
__u32 provided, calculated;
|
||||
|
||||
if (!f2fs_enable_inode_chksum(sbi, page) ||
|
||||
PageDirty(page) || PageWriteback(page))
|
||||
return true;
|
||||
|
||||
ri = &F2FS_NODE(page)->i;
|
||||
provided = le32_to_cpu(ri->i_inode_checksum);
|
||||
calculated = f2fs_inode_chksum(sbi, page);
|
||||
|
||||
if (provided != calculated)
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"checksum invalid, ino = %x, %x vs. %x",
|
||||
ino_of_node(page), provided, calculated);
|
||||
|
||||
return provided == calculated;
|
||||
}
|
||||
|
||||
void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
|
||||
{
|
||||
struct f2fs_inode *ri = &F2FS_NODE(page)->i;
|
||||
|
||||
if (!f2fs_enable_inode_chksum(sbi, page))
|
||||
return;
|
||||
|
||||
ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
|
||||
}
|
||||
|
||||
static int do_read_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct page *node_page;
|
||||
struct f2fs_inode *ri;
|
||||
projid_t i_projid;
|
||||
|
||||
/* Check if ino is within scope */
|
||||
if (check_nid_range(sbi, inode->i_ino)) {
|
||||
@@ -129,7 +206,7 @@ static int do_read_inode(struct inode *inode)
|
||||
i_gid_write(inode, le32_to_cpu(ri->i_gid));
|
||||
set_nlink(inode, le32_to_cpu(ri->i_links));
|
||||
inode->i_size = le64_to_cpu(ri->i_size);
|
||||
inode->i_blocks = le64_to_cpu(ri->i_blocks);
|
||||
inode->i_blocks = SECTOR_FROM_BLOCK(le64_to_cpu(ri->i_blocks) - 1);
|
||||
|
||||
inode->i_atime.tv_sec = le64_to_cpu(ri->i_atime);
|
||||
inode->i_ctime.tv_sec = le64_to_cpu(ri->i_ctime);
|
||||
@@ -152,6 +229,9 @@ static int do_read_inode(struct inode *inode)
|
||||
|
||||
get_inline_info(inode, ri);
|
||||
|
||||
fi->i_extra_isize = f2fs_has_extra_attr(inode) ?
|
||||
le16_to_cpu(ri->i_extra_isize) : 0;
|
||||
|
||||
/* check data exist */
|
||||
if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
|
||||
__recover_inline_status(inode, node_page);
|
||||
@@ -165,6 +245,16 @@ static int do_read_inode(struct inode *inode)
|
||||
if (!need_inode_block_update(sbi, inode->i_ino))
|
||||
fi->last_disk_size = inode->i_size;
|
||||
|
||||
if (fi->i_flags & FS_PROJINHERIT_FL)
|
||||
set_inode_flag(inode, FI_PROJ_INHERIT);
|
||||
|
||||
if (f2fs_has_extra_attr(inode) && f2fs_sb_has_project_quota(sbi->sb) &&
|
||||
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid))
|
||||
i_projid = (projid_t)le32_to_cpu(ri->i_projid);
|
||||
else
|
||||
i_projid = F2FS_DEF_PROJID;
|
||||
fi->i_projid = make_kprojid(&init_user_ns, i_projid);
|
||||
|
||||
f2fs_put_page(node_page, 1);
|
||||
|
||||
stat_inc_inline_xattr(inode);
|
||||
@@ -225,6 +315,7 @@ make_now:
|
||||
ret = -EIO;
|
||||
goto bad_inode;
|
||||
}
|
||||
f2fs_set_inode_flags(inode);
|
||||
unlock_new_inode(inode);
|
||||
trace_f2fs_iget(inode);
|
||||
return inode;
|
||||
@@ -252,6 +343,7 @@ retry:
|
||||
int update_inode(struct inode *inode, struct page *node_page)
|
||||
{
|
||||
struct f2fs_inode *ri;
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
|
||||
f2fs_inode_synced(inode);
|
||||
|
||||
@@ -265,13 +357,15 @@ int update_inode(struct inode *inode, struct page *node_page)
|
||||
ri->i_gid = cpu_to_le32(i_gid_read(inode));
|
||||
ri->i_links = cpu_to_le32(inode->i_nlink);
|
||||
ri->i_size = cpu_to_le64(i_size_read(inode));
|
||||
ri->i_blocks = cpu_to_le64(inode->i_blocks);
|
||||
ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(inode->i_blocks) + 1);
|
||||
|
||||
if (F2FS_I(inode)->extent_tree)
|
||||
set_raw_extent(&F2FS_I(inode)->extent_tree->largest,
|
||||
&ri->i_ext);
|
||||
else
|
||||
if (et) {
|
||||
read_lock(&et->lock);
|
||||
set_raw_extent(&et->largest, &ri->i_ext);
|
||||
read_unlock(&et->lock);
|
||||
} else {
|
||||
memset(&ri->i_ext, 0, sizeof(ri->i_ext));
|
||||
}
|
||||
set_raw_inline(inode, ri);
|
||||
|
||||
ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
|
||||
@@ -287,6 +381,20 @@ int update_inode(struct inode *inode, struct page *node_page)
|
||||
ri->i_generation = cpu_to_le32(inode->i_generation);
|
||||
ri->i_dir_level = F2FS_I(inode)->i_dir_level;
|
||||
|
||||
if (f2fs_has_extra_attr(inode)) {
|
||||
ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize);
|
||||
|
||||
if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) &&
|
||||
F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
|
||||
i_projid)) {
|
||||
projid_t i_projid;
|
||||
|
||||
i_projid = from_kprojid(&init_user_ns,
|
||||
F2FS_I(inode)->i_projid);
|
||||
ri->i_projid = cpu_to_le32(i_projid);
|
||||
}
|
||||
}
|
||||
|
||||
__set_inode_rdev(inode, ri);
|
||||
set_cold_node(inode, node_page);
|
||||
|
||||
@@ -312,7 +420,6 @@ retry:
|
||||
} else if (err != -ENOENT) {
|
||||
f2fs_stop_checkpoint(sbi, false);
|
||||
}
|
||||
f2fs_inode_synced(inode);
|
||||
return 0;
|
||||
}
|
||||
ret = update_inode(inode, node_page);
|
||||
@@ -335,7 +442,8 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
* We need to balance fs here to prevent from producing dirty node pages
|
||||
* during the urgent cleaning time when runing out of free sections.
|
||||
*/
|
||||
if (update_inode_page(inode))
|
||||
update_inode_page(inode);
|
||||
if (wbc && wbc->nr_to_write)
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
}
|
||||
@@ -368,10 +476,10 @@ void f2fs_evict_inode(struct inode *inode)
|
||||
if (inode->i_nlink || is_bad_inode(inode))
|
||||
goto no_delete;
|
||||
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(sbi, FAULT_EVICT_INODE))
|
||||
goto no_delete;
|
||||
#endif
|
||||
dquot_initialize(inode);
|
||||
|
||||
remove_ino_entry(sbi, inode->i_ino, APPEND_INO);
|
||||
remove_ino_entry(sbi, inode->i_ino, UPDATE_INO);
|
||||
|
||||
sb_start_intwrite(inode->i_sb);
|
||||
set_inode_flag(inode, FI_NO_ALLOC);
|
||||
@@ -380,10 +488,18 @@ retry:
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
err = f2fs_truncate(inode);
|
||||
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(sbi, FAULT_EVICT_INODE)) {
|
||||
f2fs_show_injection_info(FAULT_EVICT_INODE);
|
||||
err = -EIO;
|
||||
}
|
||||
#endif
|
||||
if (!err) {
|
||||
f2fs_lock_op(sbi);
|
||||
err = remove_inode_page(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
if (err == -ENOENT)
|
||||
err = 0;
|
||||
}
|
||||
|
||||
/* give more chances, if ENOMEM case */
|
||||
@@ -394,25 +510,37 @@ retry:
|
||||
|
||||
if (err)
|
||||
update_inode_page(inode);
|
||||
dquot_free_inode(inode);
|
||||
sb_end_intwrite(inode->i_sb);
|
||||
no_delete:
|
||||
dquot_drop(inode);
|
||||
|
||||
stat_dec_inline_xattr(inode);
|
||||
stat_dec_inline_dir(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino);
|
||||
if (!is_set_ckpt_flags(sbi, CP_ERROR_FLAG))
|
||||
f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE));
|
||||
|
||||
/* ino == 0, if f2fs_new_inode() was failed t*/
|
||||
if (inode->i_ino)
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino,
|
||||
inode->i_ino);
|
||||
if (xnid)
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
|
||||
if (is_inode_flag_set(inode, FI_APPEND_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, APPEND_INO);
|
||||
if (is_inode_flag_set(inode, FI_UPDATE_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
|
||||
if (inode->i_nlink) {
|
||||
if (is_inode_flag_set(inode, FI_APPEND_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, APPEND_INO);
|
||||
if (is_inode_flag_set(inode, FI_UPDATE_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
|
||||
}
|
||||
if (is_inode_flag_set(inode, FI_FREE_NID)) {
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
clear_inode_flag(inode, FI_FREE_NID);
|
||||
} else {
|
||||
f2fs_bug_on(sbi, err &&
|
||||
!exist_written_data(sbi, inode->i_ino, ORPHAN_INO));
|
||||
}
|
||||
f2fs_bug_on(sbi, err &&
|
||||
!exist_written_data(sbi, inode->i_ino, ORPHAN_INO));
|
||||
out_clear:
|
||||
fscrypt_put_encryption_info(inode, NULL);
|
||||
clear_inode(inode);
|
||||
@@ -424,6 +552,19 @@ void handle_failed_inode(struct inode *inode)
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct node_info ni;
|
||||
|
||||
/*
|
||||
* clear nlink of inode in order to release resource of inode
|
||||
* immediately.
|
||||
*/
|
||||
clear_nlink(inode);
|
||||
|
||||
/*
|
||||
* we must call this to avoid inode being remained as dirty, resulting
|
||||
* in a panic when flushing dirty inodes in gdirty_list.
|
||||
*/
|
||||
update_inode_page(inode);
|
||||
f2fs_inode_synced(inode);
|
||||
|
||||
/* don't make bad inode, since it becomes a regular file. */
|
||||
unlock_new_inode(inode);
|
||||
|
||||
|
||||
+136
-54
@@ -15,6 +15,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/quotaops.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -42,6 +43,8 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
|
||||
}
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
nid_free = true;
|
||||
|
||||
inode_init_owner(inode, dir, mode);
|
||||
|
||||
inode->i_ino = ino;
|
||||
@@ -52,16 +55,35 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
|
||||
err = insert_inode_locked(inode);
|
||||
if (err) {
|
||||
err = -EINVAL;
|
||||
nid_free = true;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (f2fs_sb_has_project_quota(sbi->sb) &&
|
||||
(F2FS_I(dir)->i_flags & FS_PROJINHERIT_FL))
|
||||
F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid;
|
||||
else
|
||||
F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns,
|
||||
F2FS_DEF_PROJID);
|
||||
|
||||
err = dquot_initialize(inode);
|
||||
if (err)
|
||||
goto fail_drop;
|
||||
|
||||
err = dquot_alloc_inode(inode);
|
||||
if (err)
|
||||
goto fail_drop;
|
||||
|
||||
/* If the directory encrypted, then we should encrypt the inode. */
|
||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
|
||||
set_inode_flag(inode, FI_NEW_INODE);
|
||||
|
||||
if (f2fs_sb_has_extra_attr(sbi->sb)) {
|
||||
set_inode_flag(inode, FI_EXTRA_ATTR);
|
||||
F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE;
|
||||
}
|
||||
|
||||
if (test_opt(sbi, INLINE_XATTR))
|
||||
set_inode_flag(inode, FI_INLINE_XATTR);
|
||||
if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode))
|
||||
@@ -75,6 +97,15 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
|
||||
stat_inc_inline_inode(inode);
|
||||
stat_inc_inline_dir(inode);
|
||||
|
||||
F2FS_I(inode)->i_flags =
|
||||
f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED);
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
F2FS_I(inode)->i_flags |= FS_INDEX_FL;
|
||||
|
||||
if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL)
|
||||
set_inode_flag(inode, FI_PROJ_INHERIT);
|
||||
|
||||
trace_f2fs_new_inode(inode, 0);
|
||||
return inode;
|
||||
|
||||
@@ -85,6 +116,16 @@ fail:
|
||||
set_inode_flag(inode, FI_FREE_NID);
|
||||
iput(inode);
|
||||
return ERR_PTR(err);
|
||||
fail_drop:
|
||||
trace_f2fs_new_inode(inode, err);
|
||||
dquot_drop(inode);
|
||||
inode->i_flags |= S_NOQUOTA;
|
||||
if (nid_free)
|
||||
set_inode_flag(inode, FI_FREE_NID);
|
||||
clear_nlink(inode);
|
||||
unlock_new_inode(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int is_multimedia_file(const unsigned char *s, const char *sub)
|
||||
@@ -136,6 +177,10 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
nid_t ino = 0;
|
||||
int err;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
@@ -148,8 +193,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
ino = inode->i_ino;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
if (err)
|
||||
@@ -163,6 +206,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
@@ -180,6 +225,15 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
!fscrypt_has_permitted_context(dir, inode))
|
||||
return -EPERM;
|
||||
|
||||
if (is_inode_flag_set(dir, FI_PROJ_INHERIT) &&
|
||||
(!projid_eq(F2FS_I(dir)->i_projid,
|
||||
F2FS_I(old_dentry->d_inode)->i_projid)))
|
||||
return -EXDEV;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
inode->i_ctime = current_time(inode);
|
||||
@@ -233,6 +287,10 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino)
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
@@ -321,12 +379,13 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
if (!IS_ERR(inode) && f2fs_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
bool nokey = f2fs_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
err = nokey ? -ENOKEY : -EPERM;
|
||||
if (f2fs_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
f2fs_msg(inode->i_sb, KERN_WARNING,
|
||||
"Inconsistent encryption contexts: %lu/%lu",
|
||||
dir->i_ino, inode->i_ino);
|
||||
err = -EPERM;
|
||||
goto err_out;
|
||||
}
|
||||
return d_splice_alias(inode, dentry);
|
||||
@@ -346,6 +405,10 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
trace_f2fs_unlink_enter(dir, dentry);
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
de = f2fs_find_entry(dir, &dentry->d_name, &page);
|
||||
if (!de) {
|
||||
if (IS_ERR(page))
|
||||
@@ -403,7 +466,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
return err;
|
||||
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return -EPERM;
|
||||
return -ENOKEY;
|
||||
|
||||
disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
|
||||
sizeof(struct fscrypt_symlink_data));
|
||||
@@ -412,6 +475,10 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
if (disk_link.len > dir->i_sb->s_blocksize)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
@@ -423,8 +490,6 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
if (err)
|
||||
@@ -447,7 +512,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
goto err_out;
|
||||
|
||||
if (!fscrypt_has_encryption_key(inode)) {
|
||||
err = -EPERM;
|
||||
err = -ENOKEY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
@@ -487,6 +552,8 @@ err_out:
|
||||
}
|
||||
|
||||
kfree(sd);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return err;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
@@ -499,6 +566,10 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
inode = f2fs_new_inode(dir, S_IFDIR | mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
@@ -508,8 +579,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
set_inode_flag(inode, FI_INC_LINK);
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
@@ -524,6 +593,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
@@ -547,6 +618,10 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
struct inode *inode;
|
||||
int err = 0;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
@@ -554,8 +629,6 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
inode->i_op = &f2fs_special_inode_operations;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
if (err)
|
||||
@@ -569,6 +642,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
@@ -582,6 +657,10 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
@@ -595,8 +674,6 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
}
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (err)
|
||||
@@ -622,6 +699,8 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
/* link_count was changed by d_tmpfile as well. */
|
||||
f2fs_unlock_op(sbi);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
|
||||
release_out:
|
||||
@@ -675,6 +754,19 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
||||
(!projid_eq(F2FS_I(new_dir)->i_projid,
|
||||
F2FS_I(old_dentry->d_inode)->i_projid)))
|
||||
return -EXDEV;
|
||||
|
||||
err = dquot_initialize(old_dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = dquot_initialize(new_dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
if (!old_entry) {
|
||||
if (IS_ERR(old_page))
|
||||
@@ -720,13 +812,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
if (err)
|
||||
goto put_out_dir;
|
||||
|
||||
err = update_dent_inode(old_inode, new_inode,
|
||||
&new_dentry->d_name);
|
||||
if (err) {
|
||||
release_orphan_inode(sbi);
|
||||
goto put_out_dir;
|
||||
}
|
||||
|
||||
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
|
||||
|
||||
new_inode->i_ctime = current_time(new_inode);
|
||||
@@ -778,13 +863,14 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
}
|
||||
|
||||
down_write(&F2FS_I(old_inode)->i_sem);
|
||||
file_lost_pino(old_inode);
|
||||
if (new_inode && file_enc_name(new_inode))
|
||||
file_set_enc_name(old_inode);
|
||||
if (!old_dir_entry || whiteout)
|
||||
file_lost_pino(old_inode);
|
||||
else
|
||||
F2FS_I(old_inode)->i_pino = new_dir->i_ino;
|
||||
up_write(&F2FS_I(old_inode)->i_sem);
|
||||
|
||||
old_inode->i_ctime = current_time(old_inode);
|
||||
f2fs_mark_inode_dirty_sync(old_inode);
|
||||
f2fs_mark_inode_dirty_sync(old_inode, false);
|
||||
|
||||
f2fs_delete_entry(old_entry, old_page, old_dir, NULL);
|
||||
|
||||
@@ -861,6 +947,22 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
!fscrypt_has_permitted_context(old_dir, new_inode)))
|
||||
return -EPERM;
|
||||
|
||||
if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
||||
!projid_eq(F2FS_I(new_dir)->i_projid,
|
||||
F2FS_I(old_dentry->d_inode)->i_projid)) ||
|
||||
(is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
||||
!projid_eq(F2FS_I(old_dir)->i_projid,
|
||||
F2FS_I(new_dentry->d_inode)->i_projid)))
|
||||
return -EXDEV;
|
||||
|
||||
err = dquot_initialize(old_dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = dquot_initialize(new_dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
if (!old_entry) {
|
||||
if (IS_ERR(old_page))
|
||||
@@ -908,8 +1010,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
old_nlink = old_dir_entry ? -1 : 1;
|
||||
new_nlink = -old_nlink;
|
||||
err = -EMLINK;
|
||||
if ((old_nlink > 0 && old_inode->i_nlink >= F2FS_LINK_MAX) ||
|
||||
(new_nlink > 0 && new_inode->i_nlink >= F2FS_LINK_MAX))
|
||||
if ((old_nlink > 0 && old_dir->i_nlink >= F2FS_LINK_MAX) ||
|
||||
(new_nlink > 0 && new_dir->i_nlink >= F2FS_LINK_MAX))
|
||||
goto out_new_dir;
|
||||
}
|
||||
|
||||
@@ -917,18 +1019,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
if (file_enc_name(new_inode))
|
||||
file_set_enc_name(old_inode);
|
||||
|
||||
err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name);
|
||||
if (err)
|
||||
goto out_undo;
|
||||
if (file_enc_name(old_inode))
|
||||
file_set_enc_name(new_inode);
|
||||
|
||||
/* update ".." directory entry info of old dentry */
|
||||
if (old_dir_entry)
|
||||
f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir);
|
||||
@@ -950,7 +1040,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
f2fs_i_links_write(old_dir, old_nlink > 0);
|
||||
up_write(&F2FS_I(old_dir)->i_sem);
|
||||
}
|
||||
f2fs_mark_inode_dirty_sync(old_dir);
|
||||
f2fs_mark_inode_dirty_sync(old_dir, false);
|
||||
|
||||
/* update directory entry info of new dir inode */
|
||||
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
|
||||
@@ -965,21 +1055,13 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
f2fs_i_links_write(new_dir, new_nlink > 0);
|
||||
up_write(&F2FS_I(new_dir)->i_sem);
|
||||
}
|
||||
f2fs_mark_inode_dirty_sync(new_dir);
|
||||
f2fs_mark_inode_dirty_sync(new_dir, false);
|
||||
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
out_undo:
|
||||
/*
|
||||
* Still we may fail to recover name info of f2fs_inode here
|
||||
* Drop it, once its name is set as encrypted
|
||||
*/
|
||||
update_dent_inode(old_inode, old_inode, &old_dentry->d_name);
|
||||
out_unlock:
|
||||
f2fs_unlock_op(sbi);
|
||||
out_new_dir:
|
||||
if (new_dir_entry) {
|
||||
f2fs_dentry_kunmap(new_inode, new_dir_page);
|
||||
|
||||
+699
-278
File diff suppressed because it is too large
Load Diff
+42
-39
@@ -9,10 +9,10 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
/* start node id of a node block dedicated to the given node id */
|
||||
#define START_NID(nid) ((nid / NAT_ENTRY_PER_BLOCK) * NAT_ENTRY_PER_BLOCK)
|
||||
#define START_NID(nid) (((nid) / NAT_ENTRY_PER_BLOCK) * NAT_ENTRY_PER_BLOCK)
|
||||
|
||||
/* node block offset on the NAT area dedicated to the given start node id */
|
||||
#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
|
||||
#define NAT_BLOCK_OFFSET(start_nid) ((start_nid) / NAT_ENTRY_PER_BLOCK)
|
||||
|
||||
/* # of pages to perform synchronous readahead before building free nids */
|
||||
#define FREE_NID_PAGES 8
|
||||
@@ -62,16 +62,16 @@ struct nat_entry {
|
||||
struct node_info ni; /* in-memory node information */
|
||||
};
|
||||
|
||||
#define nat_get_nid(nat) (nat->ni.nid)
|
||||
#define nat_set_nid(nat, n) (nat->ni.nid = n)
|
||||
#define nat_get_blkaddr(nat) (nat->ni.blk_addr)
|
||||
#define nat_set_blkaddr(nat, b) (nat->ni.blk_addr = b)
|
||||
#define nat_get_ino(nat) (nat->ni.ino)
|
||||
#define nat_set_ino(nat, i) (nat->ni.ino = i)
|
||||
#define nat_get_version(nat) (nat->ni.version)
|
||||
#define nat_set_version(nat, v) (nat->ni.version = v)
|
||||
#define nat_get_nid(nat) ((nat)->ni.nid)
|
||||
#define nat_set_nid(nat, n) ((nat)->ni.nid = (n))
|
||||
#define nat_get_blkaddr(nat) ((nat)->ni.blk_addr)
|
||||
#define nat_set_blkaddr(nat, b) ((nat)->ni.blk_addr = (b))
|
||||
#define nat_get_ino(nat) ((nat)->ni.ino)
|
||||
#define nat_set_ino(nat, i) ((nat)->ni.ino = (i))
|
||||
#define nat_get_version(nat) ((nat)->ni.version)
|
||||
#define nat_set_version(nat, v) ((nat)->ni.version = (v))
|
||||
|
||||
#define inc_node_version(version) (++version)
|
||||
#define inc_node_version(version) (++(version))
|
||||
|
||||
static inline void copy_node_info(struct node_info *dst,
|
||||
struct node_info *src)
|
||||
@@ -169,14 +169,15 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid)
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct free_nid *fnid;
|
||||
|
||||
spin_lock(&nm_i->free_nid_list_lock);
|
||||
if (nm_i->fcnt <= 0) {
|
||||
spin_unlock(&nm_i->free_nid_list_lock);
|
||||
spin_lock(&nm_i->nid_list_lock);
|
||||
if (nm_i->nid_cnt[FREE_NID_LIST] <= 0) {
|
||||
spin_unlock(&nm_i->nid_list_lock);
|
||||
return;
|
||||
}
|
||||
fnid = list_entry(nm_i->free_nid_list.next, struct free_nid, list);
|
||||
fnid = list_first_entry(&nm_i->nid_list[FREE_NID_LIST],
|
||||
struct free_nid, list);
|
||||
*nid = fnid->nid;
|
||||
spin_unlock(&nm_i->free_nid_list_lock);
|
||||
spin_unlock(&nm_i->nid_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -185,6 +186,12 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid)
|
||||
static inline void get_nat_bitmap(struct f2fs_sb_info *sbi, void *addr)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
if (memcmp(nm_i->nat_bitmap, nm_i->nat_bitmap_mir,
|
||||
nm_i->bitmap_size))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
#endif
|
||||
memcpy(addr, nm_i->nat_bitmap, nm_i->bitmap_size);
|
||||
}
|
||||
|
||||
@@ -193,13 +200,16 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
pgoff_t block_off;
|
||||
pgoff_t block_addr;
|
||||
int seg_off;
|
||||
|
||||
/*
|
||||
* block_off = segment_off * 512 + off_in_segment
|
||||
* OLD = (segment_off * 512) * 2 + off_in_segment
|
||||
* NEW = 2 * (segment_off * 512 + off_in_segment) - off_in_segment
|
||||
*/
|
||||
block_off = NAT_BLOCK_OFFSET(start);
|
||||
seg_off = block_off >> sbi->log_blocks_per_seg;
|
||||
|
||||
block_addr = (pgoff_t)(nm_i->nat_blkaddr +
|
||||
(seg_off << sbi->log_blocks_per_seg << 1) +
|
||||
(block_off << 1) -
|
||||
(block_off & (sbi->blocks_per_seg - 1)));
|
||||
|
||||
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
|
||||
@@ -214,11 +224,7 @@ static inline pgoff_t next_nat_addr(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
|
||||
block_addr -= nm_i->nat_blkaddr;
|
||||
if ((block_addr >> sbi->log_blocks_per_seg) % 2)
|
||||
block_addr -= sbi->blocks_per_seg;
|
||||
else
|
||||
block_addr += sbi->blocks_per_seg;
|
||||
|
||||
block_addr ^= 1 << sbi->log_blocks_per_seg;
|
||||
return block_addr + nm_i->nat_blkaddr;
|
||||
}
|
||||
|
||||
@@ -227,6 +233,9 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid)
|
||||
unsigned int block_off = NAT_BLOCK_OFFSET(start_nid);
|
||||
|
||||
f2fs_change_bit(block_off, nm_i->nat_bitmap);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
f2fs_change_bit(block_off, nm_i->nat_bitmap_mir);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline nid_t ino_of_node(struct page *node_page)
|
||||
@@ -290,14 +299,11 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr)
|
||||
{
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page));
|
||||
struct f2fs_node *rn = F2FS_NODE(page);
|
||||
size_t crc_offset = le32_to_cpu(ckpt->checksum_offset);
|
||||
__u64 cp_ver = le64_to_cpu(ckpt->checkpoint_ver);
|
||||
__u64 cp_ver = cur_cp_version(ckpt);
|
||||
|
||||
if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG))
|
||||
cp_ver |= (cur_cp_crc(ckpt) << 32);
|
||||
|
||||
if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) {
|
||||
__u64 crc = le32_to_cpu(*((__le32 *)
|
||||
((unsigned char *)ckpt + crc_offset)));
|
||||
cp_ver |= (crc << 32);
|
||||
}
|
||||
rn->footer.cp_ver = cpu_to_le64(cp_ver);
|
||||
rn->footer.next_blkaddr = cpu_to_le32(blkaddr);
|
||||
}
|
||||
@@ -305,15 +311,12 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr)
|
||||
static inline bool is_recoverable_dnode(struct page *page)
|
||||
{
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page));
|
||||
size_t crc_offset = le32_to_cpu(ckpt->checksum_offset);
|
||||
__u64 cp_ver = cur_cp_version(ckpt);
|
||||
|
||||
if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) {
|
||||
__u64 crc = le32_to_cpu(*((__le32 *)
|
||||
((unsigned char *)ckpt + crc_offset)));
|
||||
cp_ver |= (crc << 32);
|
||||
}
|
||||
return cpu_to_le64(cp_ver) == cpver_of_node(page);
|
||||
if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG))
|
||||
cp_ver |= (cur_cp_crc(ckpt) << 32);
|
||||
|
||||
return cp_ver == cpver_of_node(page);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -342,7 +345,7 @@ static inline bool IS_DNODE(struct page *node_page)
|
||||
unsigned int ofs = ofs_of_node(node_page);
|
||||
|
||||
if (f2fs_has_xattr_block(ofs))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
|
||||
ofs == 5 + 2 * NIDS_PER_BLOCK)
|
||||
|
||||
+89
-59
@@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
|
||||
}
|
||||
|
||||
static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi,
|
||||
struct list_head *head, nid_t ino)
|
||||
struct list_head *head, nid_t ino, bool quota_inode)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct fsync_inode_entry *entry;
|
||||
int err;
|
||||
|
||||
inode = f2fs_iget_retry(sbi->sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
err = dquot_initialize(inode);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
if (quota_inode) {
|
||||
err = dquot_alloc_inode(inode);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
|
||||
entry->inode = inode;
|
||||
list_add_tail(&entry->list, head);
|
||||
|
||||
return entry;
|
||||
err_out:
|
||||
iput(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void del_fsync_inode(struct fsync_inode_entry *entry)
|
||||
@@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
|
||||
|
||||
entry = get_fsync_inode(dir_list, pino);
|
||||
if (!entry) {
|
||||
entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino);
|
||||
entry = add_fsync_inode(F2FS_I_SB(inode), dir_list,
|
||||
pino, false);
|
||||
if (IS_ERR(entry)) {
|
||||
dir = ERR_CAST(entry);
|
||||
err = PTR_ERR(entry);
|
||||
@@ -140,6 +155,13 @@ retry:
|
||||
err = -EEXIST;
|
||||
goto out_unmap_put;
|
||||
}
|
||||
|
||||
err = dquot_initialize(einode);
|
||||
if (err) {
|
||||
iput(einode);
|
||||
goto out_unmap_put;
|
||||
}
|
||||
|
||||
err = acquire_orphan_inode(F2FS_I_SB(inode));
|
||||
if (err) {
|
||||
iput(einode);
|
||||
@@ -180,13 +202,15 @@ static void recover_inode(struct inode *inode, struct page *page)
|
||||
|
||||
inode->i_mode = le16_to_cpu(raw->i_mode);
|
||||
f2fs_i_size_write(inode, le64_to_cpu(raw->i_size));
|
||||
inode->i_atime.tv_sec = le64_to_cpu(raw->i_mtime);
|
||||
inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime);
|
||||
inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime);
|
||||
inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime);
|
||||
inode->i_atime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
||||
inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec);
|
||||
inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec);
|
||||
inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
||||
|
||||
F2FS_I(inode)->i_advise = raw->i_advise;
|
||||
|
||||
if (file_enc_name(inode))
|
||||
name = "<encrypted>";
|
||||
else
|
||||
@@ -196,33 +220,8 @@ static void recover_inode(struct inode *inode, struct page *page)
|
||||
ino_of_node(page), name);
|
||||
}
|
||||
|
||||
static bool is_same_inode(struct inode *inode, struct page *ipage)
|
||||
{
|
||||
struct f2fs_inode *ri = F2FS_INODE(ipage);
|
||||
struct timespec disk;
|
||||
|
||||
if (!IS_INODE(ipage))
|
||||
return true;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_ctime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec);
|
||||
if (timespec_compare(&inode->i_ctime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_atime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec);
|
||||
if (timespec_compare(&inode->i_atime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_mtime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec);
|
||||
if (timespec_compare(&inode->i_mtime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
||||
bool check_only)
|
||||
{
|
||||
struct curseg_info *curseg;
|
||||
struct page *page = NULL;
|
||||
@@ -248,21 +247,23 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
goto next;
|
||||
|
||||
entry = get_fsync_inode(head, ino_of_node(page));
|
||||
if (entry) {
|
||||
if (!is_same_inode(entry->inode, page))
|
||||
goto next;
|
||||
} else {
|
||||
if (IS_INODE(page) && is_dent_dnode(page)) {
|
||||
if (!entry) {
|
||||
bool quota_inode = false;
|
||||
|
||||
if (!check_only &&
|
||||
IS_INODE(page) && is_dent_dnode(page)) {
|
||||
err = recover_inode_page(sbi, page);
|
||||
if (err)
|
||||
break;
|
||||
quota_inode = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* CP | dnode(F) | inode(DF)
|
||||
* For this case, we should not give up now.
|
||||
*/
|
||||
entry = add_fsync_inode(sbi, head, ino_of_node(page));
|
||||
entry = add_fsync_inode(sbi, head, ino_of_node(page),
|
||||
quota_inode);
|
||||
if (IS_ERR(entry)) {
|
||||
err = PTR_ERR(entry);
|
||||
if (err == -ENOENT) {
|
||||
@@ -353,10 +354,18 @@ got_it:
|
||||
f2fs_put_page(node_page, 1);
|
||||
|
||||
if (ino != dn->inode->i_ino) {
|
||||
int ret;
|
||||
|
||||
/* Deallocate previous index in the node page */
|
||||
inode = f2fs_iget_retry(sbi->sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
ret = dquot_initialize(inode);
|
||||
if (ret) {
|
||||
iput(inode);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
inode = dn->inode;
|
||||
}
|
||||
@@ -386,7 +395,8 @@ out:
|
||||
return 0;
|
||||
|
||||
truncate_out:
|
||||
if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr)
|
||||
if (datablock_addr(tdn.inode, tdn.node_page,
|
||||
tdn.ofs_in_node) == blkaddr)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
if (dn->inode->i_ino == nid && !dn->inode_page_locked)
|
||||
unlock_page(dn->inode_page);
|
||||
@@ -405,11 +415,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
if (IS_INODE(page)) {
|
||||
recover_inline_xattr(inode, page);
|
||||
} else if (f2fs_has_xattr_block(ofs_of_node(page))) {
|
||||
/*
|
||||
* Deprecated; xattr blocks should be found from cold log.
|
||||
* But, we should remain this for backward compatibility.
|
||||
*/
|
||||
recover_xattr_data(inode, page, blkaddr);
|
||||
err = recover_xattr_data(inode, page, blkaddr);
|
||||
if (!err)
|
||||
recovered++;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -441,8 +449,8 @@ retry_dn:
|
||||
for (; start < end; start++, dn.ofs_in_node++) {
|
||||
block_t src, dest;
|
||||
|
||||
src = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||
dest = datablock_addr(page, dn.ofs_in_node);
|
||||
src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node);
|
||||
dest = datablock_addr(dn.inode, page, dn.ofs_in_node);
|
||||
|
||||
/* skip recovering if dest is the same as src */
|
||||
if (src == dest)
|
||||
@@ -454,8 +462,10 @@ retry_dn:
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((start + 1) << PAGE_SHIFT > i_size_read(inode))
|
||||
f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT);
|
||||
if (!file_keep_isize(inode) &&
|
||||
(i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT)))
|
||||
f2fs_i_size_write(inode,
|
||||
(loff_t)(start + 1) << PAGE_SHIFT);
|
||||
|
||||
/*
|
||||
* dest is reserved block, invalidate src block
|
||||
@@ -507,8 +517,10 @@ err:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
f2fs_msg(sbi->sb, KERN_NOTICE,
|
||||
"recover_data: ino = %lx, recovered = %d blocks, err = %d",
|
||||
inode->i_ino, recovered, err);
|
||||
"recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d",
|
||||
inode->i_ino,
|
||||
file_keep_isize(inode) ? "keep" : "recover",
|
||||
recovered, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -576,18 +588,31 @@ next:
|
||||
|
||||
int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
||||
struct list_head inode_list;
|
||||
struct list_head dir_list;
|
||||
block_t blkaddr;
|
||||
int err;
|
||||
int ret = 0;
|
||||
unsigned long s_flags = sbi->sb->s_flags;
|
||||
bool need_writecp = false;
|
||||
|
||||
if (s_flags & MS_RDONLY) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
|
||||
sbi->sb->s_flags &= ~MS_RDONLY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Needed for iput() to work correctly and not trash data */
|
||||
sbi->sb->s_flags |= MS_ACTIVE;
|
||||
/* Turn on quotas so that they are updated correctly */
|
||||
f2fs_enable_quota_files(sbi);
|
||||
#endif
|
||||
|
||||
fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
|
||||
sizeof(struct fsync_inode_entry));
|
||||
if (!fsync_entry_slab)
|
||||
return -ENOMEM;
|
||||
if (!fsync_entry_slab) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&inode_list);
|
||||
INIT_LIST_HEAD(&dir_list);
|
||||
@@ -595,16 +620,14 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
||||
/* prevent checkpoint */
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
|
||||
/* step #1: find fsynced inode numbers */
|
||||
err = find_fsync_dnodes(sbi, &inode_list);
|
||||
err = find_fsync_dnodes(sbi, &inode_list, check_only);
|
||||
if (err || list_empty(&inode_list))
|
||||
goto out;
|
||||
goto skip;
|
||||
|
||||
if (check_only) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
need_writecp = true;
|
||||
@@ -613,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
||||
err = recover_data(sbi, &inode_list, &dir_list);
|
||||
if (!err)
|
||||
f2fs_bug_on(sbi, !list_empty(&inode_list));
|
||||
out:
|
||||
skip:
|
||||
destroy_fsync_dnodes(&inode_list);
|
||||
|
||||
/* truncate meta pages to be used by the recovery */
|
||||
@@ -639,5 +662,12 @@ out:
|
||||
}
|
||||
|
||||
kmem_cache_destroy(fsync_entry_slab);
|
||||
out:
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Turn quotas off */
|
||||
f2fs_quota_off_umount(sbi->sb);
|
||||
#endif
|
||||
sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
|
||||
|
||||
return ret ? ret: err;
|
||||
}
|
||||
|
||||
+1245
-303
File diff suppressed because it is too large
Load Diff
+152
-96
@@ -18,79 +18,91 @@
|
||||
#define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */
|
||||
#define DEF_MAX_RECLAIM_PREFREE_SEGMENTS 4096 /* 8GB in maximum */
|
||||
|
||||
/* L: Logical segment # in volume, R: Relative segment # in main area */
|
||||
#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno)
|
||||
#define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno)
|
||||
#define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */
|
||||
|
||||
#define IS_DATASEG(t) (t <= CURSEG_COLD_DATA)
|
||||
#define IS_NODESEG(t) (t >= CURSEG_HOT_NODE)
|
||||
/* L: Logical segment # in volume, R: Relative segment # in main area */
|
||||
#define GET_L2R_SEGNO(free_i, segno) ((segno) - (free_i)->start_segno)
|
||||
#define GET_R2L_SEGNO(free_i, segno) ((segno) + (free_i)->start_segno)
|
||||
|
||||
#define IS_DATASEG(t) ((t) <= CURSEG_COLD_DATA)
|
||||
#define IS_NODESEG(t) ((t) >= CURSEG_HOT_NODE)
|
||||
|
||||
#define IS_HOT(t) ((t) == CURSEG_HOT_NODE || (t) == CURSEG_HOT_DATA)
|
||||
#define IS_WARM(t) ((t) == CURSEG_WARM_NODE || (t) == CURSEG_WARM_DATA)
|
||||
#define IS_COLD(t) ((t) == CURSEG_COLD_NODE || (t) == CURSEG_COLD_DATA)
|
||||
|
||||
#define IS_CURSEG(sbi, seg) \
|
||||
((seg == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \
|
||||
(seg == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \
|
||||
(seg == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \
|
||||
(seg == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \
|
||||
(seg == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \
|
||||
(seg == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno))
|
||||
(((seg) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \
|
||||
((seg) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \
|
||||
((seg) == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \
|
||||
((seg) == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \
|
||||
((seg) == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \
|
||||
((seg) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno))
|
||||
|
||||
#define IS_CURSEC(sbi, secno) \
|
||||
((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \
|
||||
sbi->segs_per_sec) || \
|
||||
(secno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \
|
||||
sbi->segs_per_sec) || \
|
||||
(secno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \
|
||||
sbi->segs_per_sec) || \
|
||||
(secno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \
|
||||
sbi->segs_per_sec) || \
|
||||
(secno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \
|
||||
sbi->segs_per_sec) || \
|
||||
(secno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \
|
||||
sbi->segs_per_sec)) \
|
||||
(((secno) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \
|
||||
(sbi)->segs_per_sec) || \
|
||||
((secno) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \
|
||||
(sbi)->segs_per_sec) || \
|
||||
((secno) == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \
|
||||
(sbi)->segs_per_sec) || \
|
||||
((secno) == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \
|
||||
(sbi)->segs_per_sec) || \
|
||||
((secno) == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \
|
||||
(sbi)->segs_per_sec) || \
|
||||
((secno) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \
|
||||
(sbi)->segs_per_sec)) \
|
||||
|
||||
#define MAIN_BLKADDR(sbi) (SM_I(sbi)->main_blkaddr)
|
||||
#define SEG0_BLKADDR(sbi) (SM_I(sbi)->seg0_blkaddr)
|
||||
|
||||
#define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments)
|
||||
#define MAIN_SECS(sbi) (sbi->total_sections)
|
||||
#define MAIN_SECS(sbi) ((sbi)->total_sections)
|
||||
|
||||
#define TOTAL_SEGS(sbi) (SM_I(sbi)->segment_count)
|
||||
#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << sbi->log_blocks_per_seg)
|
||||
#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << (sbi)->log_blocks_per_seg)
|
||||
|
||||
#define MAX_BLKADDR(sbi) (SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi))
|
||||
#define SEGMENT_SIZE(sbi) (1ULL << (sbi->log_blocksize + \
|
||||
sbi->log_blocks_per_seg))
|
||||
#define SEGMENT_SIZE(sbi) (1ULL << ((sbi)->log_blocksize + \
|
||||
(sbi)->log_blocks_per_seg))
|
||||
|
||||
#define START_BLOCK(sbi, segno) (SEG0_BLKADDR(sbi) + \
|
||||
(GET_R2L_SEGNO(FREE_I(sbi), segno) << sbi->log_blocks_per_seg))
|
||||
(GET_R2L_SEGNO(FREE_I(sbi), segno) << (sbi)->log_blocks_per_seg))
|
||||
|
||||
#define NEXT_FREE_BLKADDR(sbi, curseg) \
|
||||
(START_BLOCK(sbi, curseg->segno) + curseg->next_blkoff)
|
||||
(START_BLOCK(sbi, (curseg)->segno) + (curseg)->next_blkoff)
|
||||
|
||||
#define GET_SEGOFF_FROM_SEG0(sbi, blk_addr) ((blk_addr) - SEG0_BLKADDR(sbi))
|
||||
#define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \
|
||||
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg)
|
||||
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> (sbi)->log_blocks_per_seg)
|
||||
#define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \
|
||||
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1))
|
||||
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1))
|
||||
|
||||
#define GET_SEGNO(sbi, blk_addr) \
|
||||
(((blk_addr == NULL_ADDR) || (blk_addr == NEW_ADDR)) ? \
|
||||
((((blk_addr) == NULL_ADDR) || ((blk_addr) == NEW_ADDR)) ? \
|
||||
NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \
|
||||
GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
|
||||
#define GET_SECNO(sbi, segno) \
|
||||
((segno) / sbi->segs_per_sec)
|
||||
#define GET_ZONENO_FROM_SEGNO(sbi, segno) \
|
||||
((segno / sbi->segs_per_sec) / sbi->secs_per_zone)
|
||||
#define BLKS_PER_SEC(sbi) \
|
||||
((sbi)->segs_per_sec * (sbi)->blocks_per_seg)
|
||||
#define GET_SEC_FROM_SEG(sbi, segno) \
|
||||
((segno) / (sbi)->segs_per_sec)
|
||||
#define GET_SEG_FROM_SEC(sbi, secno) \
|
||||
((secno) * (sbi)->segs_per_sec)
|
||||
#define GET_ZONE_FROM_SEC(sbi, secno) \
|
||||
((secno) / (sbi)->secs_per_zone)
|
||||
#define GET_ZONE_FROM_SEG(sbi, segno) \
|
||||
GET_ZONE_FROM_SEC(sbi, GET_SEC_FROM_SEG(sbi, segno))
|
||||
|
||||
#define GET_SUM_BLOCK(sbi, segno) \
|
||||
((sbi->sm_info->ssa_blkaddr) + segno)
|
||||
((sbi)->sm_info->ssa_blkaddr + (segno))
|
||||
|
||||
#define GET_SUM_TYPE(footer) ((footer)->entry_type)
|
||||
#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = type)
|
||||
#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = (type))
|
||||
|
||||
#define SIT_ENTRY_OFFSET(sit_i, segno) \
|
||||
(segno % sit_i->sents_per_block)
|
||||
((segno) % (sit_i)->sents_per_block)
|
||||
#define SIT_BLOCK_OFFSET(segno) \
|
||||
(segno / SIT_ENTRY_PER_BLOCK)
|
||||
((segno) / SIT_ENTRY_PER_BLOCK)
|
||||
#define START_SEGNO(segno) \
|
||||
(SIT_BLOCK_OFFSET(segno) * SIT_ENTRY_PER_BLOCK)
|
||||
#define SIT_BLK_CNT(sbi) \
|
||||
@@ -101,9 +113,7 @@
|
||||
#define SECTOR_FROM_BLOCK(blk_addr) \
|
||||
(((sector_t)blk_addr) << F2FS_LOG_SECTORS_PER_BLOCK)
|
||||
#define SECTOR_TO_BLOCK(sectors) \
|
||||
(sectors >> F2FS_LOG_SECTORS_PER_BLOCK)
|
||||
#define MAX_BIO_BLOCKS(sbi) \
|
||||
((int)min((int)max_hw_blocks(sbi), BIO_MAX_PAGES))
|
||||
((sectors) >> F2FS_LOG_SECTORS_PER_BLOCK)
|
||||
|
||||
/*
|
||||
* indicate a block allocation direction: RIGHT and LEFT.
|
||||
@@ -132,7 +142,10 @@ enum {
|
||||
*/
|
||||
enum {
|
||||
GC_CB = 0,
|
||||
GC_GREEDY
|
||||
GC_GREEDY,
|
||||
ALLOC_NEXT,
|
||||
FLUSH_DEVICE,
|
||||
MAX_GC_POLICY,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -164,6 +177,9 @@ struct seg_entry {
|
||||
unsigned int ckpt_valid_blocks:10; /* # of valid blocks last cp */
|
||||
unsigned int padding:6; /* padding */
|
||||
unsigned char *cur_valid_map; /* validity bitmap of blocks */
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
unsigned char *cur_valid_map_mir; /* mirror of current valid bitmap */
|
||||
#endif
|
||||
/*
|
||||
* # of valid blocks and the validity bitmap stored in the the last
|
||||
* checkpoint pack. This information is used by the SSR mode.
|
||||
@@ -186,9 +202,12 @@ struct segment_allocation {
|
||||
* the page is atomically written, and it is in inmem_pages list.
|
||||
*/
|
||||
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
|
||||
#define DUMMY_WRITTEN_PAGE ((unsigned long)-2)
|
||||
|
||||
#define IS_ATOMIC_WRITTEN_PAGE(page) \
|
||||
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
||||
#define IS_DUMMY_WRITTEN_PAGE(page) \
|
||||
(page_private(page) == (unsigned long)DUMMY_WRITTEN_PAGE)
|
||||
|
||||
struct inmem_pages {
|
||||
struct list_head list;
|
||||
@@ -203,6 +222,9 @@ struct sit_info {
|
||||
block_t sit_blocks; /* # of blocks used by SIT area */
|
||||
block_t written_valid_blocks; /* # of valid blocks in main area */
|
||||
char *sit_bitmap; /* SIT bitmap pointer */
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
char *sit_bitmap_mir; /* SIT bitmap mirror */
|
||||
#endif
|
||||
unsigned int bitmap_size; /* SIT bitmap size */
|
||||
|
||||
unsigned long *tmp_map; /* bitmap for temporal use */
|
||||
@@ -218,6 +240,8 @@ struct sit_info {
|
||||
unsigned long long mounted_time; /* mount time */
|
||||
unsigned long long min_mtime; /* min. modification time */
|
||||
unsigned long long max_mtime; /* max. modification time */
|
||||
|
||||
unsigned int last_victim[MAX_GC_POLICY]; /* last victim segment # */
|
||||
};
|
||||
|
||||
struct free_segmap_info {
|
||||
@@ -294,17 +318,17 @@ static inline struct sec_entry *get_sec_entry(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
return &sit_i->sec_entries[GET_SECNO(sbi, segno)];
|
||||
return &sit_i->sec_entries[GET_SEC_FROM_SEG(sbi, segno)];
|
||||
}
|
||||
|
||||
static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno, int section)
|
||||
unsigned int segno, bool use_section)
|
||||
{
|
||||
/*
|
||||
* In order to get # of valid blocks in a section instantly from many
|
||||
* segments, f2fs manages two counting structures separately.
|
||||
*/
|
||||
if (section > 1)
|
||||
if (use_section && sbi->segs_per_sec > 1)
|
||||
return get_sec_entry(sbi, segno)->valid_blocks;
|
||||
else
|
||||
return get_seg_entry(sbi, segno)->valid_blocks;
|
||||
@@ -317,6 +341,9 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se,
|
||||
se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs);
|
||||
memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
#endif
|
||||
se->type = GET_SIT_TYPE(rs);
|
||||
se->mtime = le64_to_cpu(rs->mtime);
|
||||
}
|
||||
@@ -346,8 +373,8 @@ static inline unsigned int find_next_inuse(struct free_segmap_info *free_i,
|
||||
static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int secno = segno / sbi->segs_per_sec;
|
||||
unsigned int start_segno = secno * sbi->segs_per_sec;
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);
|
||||
unsigned int next;
|
||||
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
@@ -367,7 +394,8 @@ static inline void __set_inuse(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
{
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int secno = segno / sbi->segs_per_sec;
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
|
||||
set_bit(segno, free_i->free_segmap);
|
||||
free_i->free_segments--;
|
||||
if (!test_and_set_bit(secno, free_i->free_secmap))
|
||||
@@ -378,8 +406,8 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
{
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int secno = segno / sbi->segs_per_sec;
|
||||
unsigned int start_segno = secno * sbi->segs_per_sec;
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);
|
||||
unsigned int next;
|
||||
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
@@ -400,7 +428,8 @@ static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
{
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int secno = segno / sbi->segs_per_sec;
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
if (!test_and_set_bit(segno, free_i->free_segmap)) {
|
||||
free_i->free_segments--;
|
||||
@@ -414,6 +443,12 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi,
|
||||
void *dst_addr)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir,
|
||||
sit_i->bitmap_size))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
#endif
|
||||
memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size);
|
||||
}
|
||||
|
||||
@@ -457,26 +492,9 @@ static inline int overprovision_segments(struct f2fs_sb_info *sbi)
|
||||
return SM_I(sbi)->ovp_segments;
|
||||
}
|
||||
|
||||
static inline int overprovision_sections(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return ((unsigned int) overprovision_segments(sbi)) / sbi->segs_per_sec;
|
||||
}
|
||||
|
||||
static inline int reserved_sections(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return ((unsigned int) reserved_segments(sbi)) / sbi->segs_per_sec;
|
||||
}
|
||||
|
||||
static inline bool need_SSR(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
|
||||
int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
|
||||
|
||||
if (test_opt(sbi, LFS))
|
||||
return false;
|
||||
|
||||
return free_sections(sbi) <= (node_secs + 2 * dent_secs +
|
||||
reserved_sections(sbi) + 1);
|
||||
return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi));
|
||||
}
|
||||
|
||||
static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
|
||||
@@ -484,14 +502,14 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
|
||||
{
|
||||
int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
|
||||
int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
|
||||
|
||||
node_secs += get_blocktype_secs(sbi, F2FS_DIRTY_IMETA);
|
||||
int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA);
|
||||
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
return false;
|
||||
|
||||
return (free_sections(sbi) + freed) <=
|
||||
(node_secs + 2 * dent_secs + reserved_sections(sbi) + needed);
|
||||
(node_secs + 2 * dent_secs + imeta_secs +
|
||||
reserved_sections(sbi) + needed);
|
||||
}
|
||||
|
||||
static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi)
|
||||
@@ -521,6 +539,7 @@ static inline int utilization(struct f2fs_sb_info *sbi)
|
||||
*/
|
||||
#define DEF_MIN_IPU_UTIL 70
|
||||
#define DEF_MIN_FSYNC_BLOCKS 8
|
||||
#define DEF_MIN_HOT_BLOCKS 16
|
||||
|
||||
enum {
|
||||
F2FS_IPU_FORCE,
|
||||
@@ -528,20 +547,22 @@ enum {
|
||||
F2FS_IPU_UTIL,
|
||||
F2FS_IPU_SSR_UTIL,
|
||||
F2FS_IPU_FSYNC,
|
||||
F2FS_IPU_ASYNC,
|
||||
};
|
||||
|
||||
static inline bool need_inplace_update(struct inode *inode)
|
||||
static inline bool need_inplace_update_policy(struct inode *inode,
|
||||
struct f2fs_io_info *fio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
unsigned int policy = SM_I(sbi)->ipu_policy;
|
||||
|
||||
/* IPU can be done only for the user data */
|
||||
if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
|
||||
return false;
|
||||
|
||||
if (test_opt(sbi, LFS))
|
||||
return false;
|
||||
|
||||
/* if this is cold file, we should overwrite to avoid fragmentation */
|
||||
if (file_is_cold(inode))
|
||||
return true;
|
||||
|
||||
if (policy & (0x1 << F2FS_IPU_FORCE))
|
||||
return true;
|
||||
if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi))
|
||||
@@ -553,6 +574,15 @@ static inline bool need_inplace_update(struct inode *inode)
|
||||
utilization(sbi) > SM_I(sbi)->min_ipu_util)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* IPU for rewrite async pages
|
||||
*/
|
||||
if (policy & (0x1 << F2FS_IPU_ASYNC) &&
|
||||
fio && fio->op == REQ_OP_WRITE &&
|
||||
!(fio->op_flags & REQ_SYNC) &&
|
||||
!f2fs_encrypted_inode(inode))
|
||||
return true;
|
||||
|
||||
/* this is only set during fdatasync */
|
||||
if (policy & (0x1 << F2FS_IPU_FSYNC) &&
|
||||
is_inode_flag_set(inode, FI_NEED_IPU))
|
||||
@@ -633,6 +663,12 @@ static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi,
|
||||
|
||||
check_seg_range(sbi, start);
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
if (f2fs_test_bit(offset, sit_i->sit_bitmap) !=
|
||||
f2fs_test_bit(offset, sit_i->sit_bitmap_mir))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
#endif
|
||||
|
||||
/* calculate sit block address */
|
||||
if (f2fs_test_bit(offset, sit_i->sit_bitmap))
|
||||
blk_addr += sit_i->sit_blocks;
|
||||
@@ -658,13 +694,17 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start)
|
||||
unsigned int block_off = SIT_BLOCK_OFFSET(start);
|
||||
|
||||
f2fs_change_bit(block_off, sit_i->sit_bitmap);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
f2fs_change_bit(block_off, sit_i->sit_bitmap_mir);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
return sit_i->elapsed_time + CURRENT_TIME_SEC.tv_sec -
|
||||
sit_i->mounted_time;
|
||||
time64_t now = ktime_get_real_seconds();
|
||||
|
||||
return sit_i->elapsed_time + now - sit_i->mounted_time;
|
||||
}
|
||||
|
||||
static inline void set_summary(struct f2fs_summary *sum, nid_t nid,
|
||||
@@ -691,7 +731,7 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
|
||||
static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi,
|
||||
unsigned int secno)
|
||||
{
|
||||
if (get_valid_blocks(sbi, secno, sbi->segs_per_sec) >=
|
||||
if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) >=
|
||||
sbi->fggc_threshold)
|
||||
return true;
|
||||
return false;
|
||||
@@ -704,19 +744,12 @@ static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct block_device *bdev = sbi->sb->s_bdev;
|
||||
struct request_queue *q = bdev_get_queue(bdev);
|
||||
return SECTOR_TO_BLOCK(queue_max_sectors(q));
|
||||
}
|
||||
|
||||
/*
|
||||
* It is very important to gather dirty pages and write at once, so that we can
|
||||
* submit a big bio without interfering other data writes.
|
||||
* By default, 512 pages for directory data,
|
||||
* 512 pages (2MB) * 3 for three types of nodes, and
|
||||
* max_bio_blocks for meta are set.
|
||||
* 512 pages (2MB) * 8 for nodes, and
|
||||
* 256 pages * 8 for meta are set.
|
||||
*/
|
||||
static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
@@ -728,7 +761,7 @@ static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type)
|
||||
else if (type == NODE)
|
||||
return 8 * sbi->blocks_per_seg;
|
||||
else if (type == META)
|
||||
return 8 * MAX_BIO_BLOCKS(sbi);
|
||||
return 8 * BIO_MAX_PAGES;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@@ -745,12 +778,35 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type,
|
||||
return 0;
|
||||
|
||||
nr_to_write = wbc->nr_to_write;
|
||||
|
||||
desired = BIO_MAX_PAGES;
|
||||
if (type == NODE)
|
||||
desired = 2 * max_hw_blocks(sbi);
|
||||
else
|
||||
desired = MAX_BIO_BLOCKS(sbi);
|
||||
desired <<= 1;
|
||||
|
||||
wbc->nr_to_write = desired;
|
||||
return desired - nr_to_write;
|
||||
}
|
||||
|
||||
static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force)
|
||||
{
|
||||
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
||||
bool wakeup = false;
|
||||
int i;
|
||||
|
||||
if (force)
|
||||
goto wake_up;
|
||||
|
||||
mutex_lock(&dcc->cmd_lock);
|
||||
for (i = MAX_PLIST_NUM - 1;
|
||||
i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) {
|
||||
if (!list_empty(&dcc->pend_list[i])) {
|
||||
wakeup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dcc->cmd_lock);
|
||||
if (!wakeup)
|
||||
return;
|
||||
wake_up:
|
||||
dcc->discard_wake = 1;
|
||||
wake_up_interruptible_all(&dcc->discard_wait_queue);
|
||||
}
|
||||
|
||||
+6
-4
@@ -21,14 +21,16 @@ static unsigned int shrinker_run_no;
|
||||
|
||||
static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt;
|
||||
long count = NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt;
|
||||
|
||||
return count > 0 ? count : 0;
|
||||
}
|
||||
|
||||
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (NM_I(sbi)->fcnt > MAX_FREE_NIDS)
|
||||
return NM_I(sbi)->fcnt - MAX_FREE_NIDS;
|
||||
return 0;
|
||||
long count = NM_I(sbi)->nid_cnt[FREE_NID_LIST] - MAX_FREE_NIDS;
|
||||
|
||||
return count > 0 ? count : 0;
|
||||
}
|
||||
|
||||
static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi)
|
||||
|
||||
+1069
-388
File diff suppressed because it is too large
Load Diff
+556
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* f2fs sysfs interface
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com/
|
||||
* Copyright (c) 2017 Chao Yu <chao@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "segment.h"
|
||||
#include "gc.h"
|
||||
|
||||
static struct proc_dir_entry *f2fs_proc_root;
|
||||
|
||||
/* Sysfs support for f2fs */
|
||||
enum {
|
||||
GC_THREAD, /* struct f2fs_gc_thread */
|
||||
SM_INFO, /* struct f2fs_sm_info */
|
||||
DCC_INFO, /* struct discard_cmd_control */
|
||||
NM_INFO, /* struct f2fs_nm_info */
|
||||
F2FS_SBI, /* struct f2fs_sb_info */
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
FAULT_INFO_RATE, /* struct f2fs_fault_info */
|
||||
FAULT_INFO_TYPE, /* struct f2fs_fault_info */
|
||||
#endif
|
||||
RESERVED_BLOCKS,
|
||||
};
|
||||
|
||||
struct f2fs_attr {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *);
|
||||
ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *,
|
||||
const char *, size_t);
|
||||
int struct_type;
|
||||
int offset;
|
||||
int id;
|
||||
};
|
||||
|
||||
static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
|
||||
{
|
||||
if (struct_type == GC_THREAD)
|
||||
return (unsigned char *)sbi->gc_thread;
|
||||
else if (struct_type == SM_INFO)
|
||||
return (unsigned char *)SM_I(sbi);
|
||||
else if (struct_type == DCC_INFO)
|
||||
return (unsigned char *)SM_I(sbi)->dcc_info;
|
||||
else if (struct_type == NM_INFO)
|
||||
return (unsigned char *)NM_I(sbi);
|
||||
else if (struct_type == F2FS_SBI || struct_type == RESERVED_BLOCKS)
|
||||
return (unsigned char *)sbi;
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
else if (struct_type == FAULT_INFO_RATE ||
|
||||
struct_type == FAULT_INFO_TYPE)
|
||||
return (unsigned char *)&sbi->fault_info;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
|
||||
if (!sb->s_bdev->bd_part)
|
||||
return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
(unsigned long long)(sbi->kbytes_written +
|
||||
BD_PART_WRITTEN(sbi)));
|
||||
}
|
||||
|
||||
static ssize_t features_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
int len = 0;
|
||||
|
||||
if (!sb->s_bdev->bd_part)
|
||||
return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
|
||||
if (f2fs_sb_has_crypto(sb))
|
||||
len += snprintf(buf, PAGE_SIZE - len, "%s",
|
||||
"encryption");
|
||||
if (f2fs_sb_mounted_blkzoned(sb))
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "blkzoned");
|
||||
if (f2fs_sb_has_extra_attr(sb))
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "extra_attr");
|
||||
if (f2fs_sb_has_project_quota(sb))
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "projquota");
|
||||
if (f2fs_sb_has_inode_chksum(sb))
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "inode_checksum");
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
unsigned char *ptr = NULL;
|
||||
unsigned int *ui;
|
||||
|
||||
ptr = __struct_ptr(sbi, a->struct_type);
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
|
||||
ui = (unsigned int *)(ptr + a->offset);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", *ui);
|
||||
}
|
||||
|
||||
static ssize_t f2fs_sbi_store(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
unsigned long t;
|
||||
unsigned int *ui;
|
||||
ssize_t ret;
|
||||
|
||||
ptr = __struct_ptr(sbi, a->struct_type);
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
|
||||
ui = (unsigned int *)(ptr + a->offset);
|
||||
|
||||
ret = kstrtoul(skip_spaces(buf), 0, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (a->struct_type == FAULT_INFO_TYPE && t >= (1 << FAULT_MAX))
|
||||
return -EINVAL;
|
||||
#endif
|
||||
if (a->struct_type == RESERVED_BLOCKS) {
|
||||
spin_lock(&sbi->stat_lock);
|
||||
if ((unsigned long)sbi->total_valid_block_count + t >
|
||||
(unsigned long)sbi->user_block_count) {
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
*ui = t;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "discard_granularity")) {
|
||||
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
||||
int i;
|
||||
|
||||
if (t == 0 || t > MAX_PLIST_NUM)
|
||||
return -EINVAL;
|
||||
if (t == *ui)
|
||||
return count;
|
||||
|
||||
mutex_lock(&dcc->cmd_lock);
|
||||
for (i = 0; i < MAX_PLIST_NUM; i++) {
|
||||
if (i >= t - 1)
|
||||
dcc->pend_list_tag[i] |= P_ACTIVE;
|
||||
else
|
||||
dcc->pend_list_tag[i] &= (~P_ACTIVE);
|
||||
}
|
||||
mutex_unlock(&dcc->cmd_lock);
|
||||
|
||||
*ui = t;
|
||||
return count;
|
||||
}
|
||||
|
||||
*ui = t;
|
||||
|
||||
if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0)
|
||||
f2fs_reset_iostat(sbi);
|
||||
if (!strcmp(a->attr.name, "gc_urgent") && t == 1 && sbi->gc_thread) {
|
||||
sbi->gc_thread->gc_wake = 1;
|
||||
wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head);
|
||||
wake_up_discard_thread(sbi, true);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t f2fs_attr_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
|
||||
s_kobj);
|
||||
struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
|
||||
|
||||
return a->show ? a->show(a, sbi, buf) : 0;
|
||||
}
|
||||
|
||||
static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
|
||||
s_kobj);
|
||||
struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
|
||||
|
||||
return a->store ? a->store(a, sbi, buf, len) : 0;
|
||||
}
|
||||
|
||||
static void f2fs_sb_release(struct kobject *kobj)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
|
||||
s_kobj);
|
||||
complete(&sbi->s_kobj_unregister);
|
||||
}
|
||||
|
||||
enum feat_id {
|
||||
FEAT_CRYPTO = 0,
|
||||
FEAT_BLKZONED,
|
||||
FEAT_ATOMIC_WRITE,
|
||||
FEAT_EXTRA_ATTR,
|
||||
FEAT_PROJECT_QUOTA,
|
||||
FEAT_INODE_CHECKSUM,
|
||||
};
|
||||
|
||||
static ssize_t f2fs_feature_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
switch (a->id) {
|
||||
case FEAT_CRYPTO:
|
||||
case FEAT_BLKZONED:
|
||||
case FEAT_ATOMIC_WRITE:
|
||||
case FEAT_EXTRA_ATTR:
|
||||
case FEAT_PROJECT_QUOTA:
|
||||
case FEAT_INODE_CHECKSUM:
|
||||
return snprintf(buf, PAGE_SIZE, "supported\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \
|
||||
static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
.attr = {.name = __stringify(_name), .mode = _mode }, \
|
||||
.show = _show, \
|
||||
.store = _store, \
|
||||
.struct_type = _struct_type, \
|
||||
.offset = _offset \
|
||||
}
|
||||
|
||||
#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \
|
||||
F2FS_ATTR_OFFSET(struct_type, name, 0644, \
|
||||
f2fs_sbi_show, f2fs_sbi_store, \
|
||||
offsetof(struct struct_name, elname))
|
||||
|
||||
#define F2FS_GENERAL_RO_ATTR(name) \
|
||||
static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
|
||||
|
||||
#define F2FS_FEATURE_RO_ATTR(_name, _id) \
|
||||
static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
.attr = {.name = __stringify(_name), .mode = 0444 }, \
|
||||
.show = f2fs_feature_show, \
|
||||
.id = _id, \
|
||||
}
|
||||
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent_sleep_time,
|
||||
urgent_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent, gc_urgent);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments);
|
||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards);
|
||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, discard_granularity, discard_granularity);
|
||||
F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, reserved_blocks, reserved_blocks);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks);
|
||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
|
||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages);
|
||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
|
||||
F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
|
||||
#endif
|
||||
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
|
||||
F2FS_GENERAL_RO_ATTR(features);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
|
||||
#endif
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE);
|
||||
F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR);
|
||||
F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA);
|
||||
F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM);
|
||||
|
||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||
static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(gc_urgent_sleep_time),
|
||||
ATTR_LIST(gc_min_sleep_time),
|
||||
ATTR_LIST(gc_max_sleep_time),
|
||||
ATTR_LIST(gc_no_gc_sleep_time),
|
||||
ATTR_LIST(gc_idle),
|
||||
ATTR_LIST(gc_urgent),
|
||||
ATTR_LIST(reclaim_segments),
|
||||
ATTR_LIST(max_small_discards),
|
||||
ATTR_LIST(discard_granularity),
|
||||
ATTR_LIST(batched_trim_sections),
|
||||
ATTR_LIST(ipu_policy),
|
||||
ATTR_LIST(min_ipu_util),
|
||||
ATTR_LIST(min_fsync_blocks),
|
||||
ATTR_LIST(min_hot_blocks),
|
||||
ATTR_LIST(max_victim_search),
|
||||
ATTR_LIST(dir_level),
|
||||
ATTR_LIST(ram_thresh),
|
||||
ATTR_LIST(ra_nid_pages),
|
||||
ATTR_LIST(dirty_nats_ratio),
|
||||
ATTR_LIST(cp_interval),
|
||||
ATTR_LIST(idle_interval),
|
||||
ATTR_LIST(iostat_enable),
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
ATTR_LIST(inject_rate),
|
||||
ATTR_LIST(inject_type),
|
||||
#endif
|
||||
ATTR_LIST(lifetime_write_kbytes),
|
||||
ATTR_LIST(features),
|
||||
ATTR_LIST(reserved_blocks),
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *f2fs_feat_attrs[] = {
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
ATTR_LIST(encryption),
|
||||
#endif
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
ATTR_LIST(block_zoned),
|
||||
#endif
|
||||
ATTR_LIST(atomic_write),
|
||||
ATTR_LIST(extra_attr),
|
||||
ATTR_LIST(project_quota),
|
||||
ATTR_LIST(inode_checksum),
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct sysfs_ops f2fs_attr_ops = {
|
||||
.show = f2fs_attr_show,
|
||||
.store = f2fs_attr_store,
|
||||
};
|
||||
|
||||
static struct kobj_type f2fs_sb_ktype = {
|
||||
.default_attrs = f2fs_attrs,
|
||||
.sysfs_ops = &f2fs_attr_ops,
|
||||
.release = f2fs_sb_release,
|
||||
};
|
||||
|
||||
static struct kobj_type f2fs_ktype = {
|
||||
.sysfs_ops = &f2fs_attr_ops,
|
||||
};
|
||||
|
||||
static struct kset f2fs_kset = {
|
||||
.kobj = {.ktype = &f2fs_ktype},
|
||||
};
|
||||
|
||||
static struct kobj_type f2fs_feat_ktype = {
|
||||
.default_attrs = f2fs_feat_attrs,
|
||||
.sysfs_ops = &f2fs_attr_ops,
|
||||
};
|
||||
|
||||
static struct kobject f2fs_feat = {
|
||||
.kset = &f2fs_kset,
|
||||
};
|
||||
|
||||
static int segment_info_seq_show(struct seq_file *seq, void *offset)
|
||||
{
|
||||
struct super_block *sb = seq->private;
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
unsigned int total_segs =
|
||||
le32_to_cpu(sbi->raw_super->segment_count_main);
|
||||
int i;
|
||||
|
||||
seq_puts(seq, "format: segment_type|valid_blocks\n"
|
||||
"segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n");
|
||||
|
||||
for (i = 0; i < total_segs; i++) {
|
||||
struct seg_entry *se = get_seg_entry(sbi, i);
|
||||
|
||||
if ((i % 10) == 0)
|
||||
seq_printf(seq, "%-10d", i);
|
||||
seq_printf(seq, "%d|%-3u", se->type,
|
||||
get_valid_blocks(sbi, i, false));
|
||||
if ((i % 10) == 9 || i == (total_segs - 1))
|
||||
seq_putc(seq, '\n');
|
||||
else
|
||||
seq_putc(seq, ' ');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int segment_bits_seq_show(struct seq_file *seq, void *offset)
|
||||
{
|
||||
struct super_block *sb = seq->private;
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
unsigned int total_segs =
|
||||
le32_to_cpu(sbi->raw_super->segment_count_main);
|
||||
int i, j;
|
||||
|
||||
seq_puts(seq, "format: segment_type|valid_blocks|bitmaps\n"
|
||||
"segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n");
|
||||
|
||||
for (i = 0; i < total_segs; i++) {
|
||||
struct seg_entry *se = get_seg_entry(sbi, i);
|
||||
|
||||
seq_printf(seq, "%-10d", i);
|
||||
seq_printf(seq, "%d|%-3u|", se->type,
|
||||
get_valid_blocks(sbi, i, false));
|
||||
for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++)
|
||||
seq_printf(seq, " %.2x", se->cur_valid_map[j]);
|
||||
seq_putc(seq, '\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iostat_info_seq_show(struct seq_file *seq, void *offset)
|
||||
{
|
||||
struct super_block *sb = seq->private;
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
time64_t now = ktime_get_real_seconds();
|
||||
|
||||
if (!sbi->iostat_enable)
|
||||
return 0;
|
||||
|
||||
seq_printf(seq, "time: %-16llu\n", now);
|
||||
|
||||
/* print app IOs */
|
||||
seq_printf(seq, "app buffered: %-16llu\n",
|
||||
sbi->write_iostat[APP_BUFFERED_IO]);
|
||||
seq_printf(seq, "app direct: %-16llu\n",
|
||||
sbi->write_iostat[APP_DIRECT_IO]);
|
||||
seq_printf(seq, "app mapped: %-16llu\n",
|
||||
sbi->write_iostat[APP_MAPPED_IO]);
|
||||
|
||||
/* print fs IOs */
|
||||
seq_printf(seq, "fs data: %-16llu\n",
|
||||
sbi->write_iostat[FS_DATA_IO]);
|
||||
seq_printf(seq, "fs node: %-16llu\n",
|
||||
sbi->write_iostat[FS_NODE_IO]);
|
||||
seq_printf(seq, "fs meta: %-16llu\n",
|
||||
sbi->write_iostat[FS_META_IO]);
|
||||
seq_printf(seq, "fs gc data: %-16llu\n",
|
||||
sbi->write_iostat[FS_GC_DATA_IO]);
|
||||
seq_printf(seq, "fs gc node: %-16llu\n",
|
||||
sbi->write_iostat[FS_GC_NODE_IO]);
|
||||
seq_printf(seq, "fs cp data: %-16llu\n",
|
||||
sbi->write_iostat[FS_CP_DATA_IO]);
|
||||
seq_printf(seq, "fs cp node: %-16llu\n",
|
||||
sbi->write_iostat[FS_CP_NODE_IO]);
|
||||
seq_printf(seq, "fs cp meta: %-16llu\n",
|
||||
sbi->write_iostat[FS_CP_META_IO]);
|
||||
seq_printf(seq, "fs discard: %-16llu\n",
|
||||
sbi->write_iostat[FS_DISCARD]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define F2FS_PROC_FILE_DEF(_name) \
|
||||
static int _name##_open_fs(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
return single_open(file, _name##_seq_show, PDE_DATA(inode)); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations f2fs_seq_##_name##_fops = { \
|
||||
.open = _name##_open_fs, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
};
|
||||
|
||||
F2FS_PROC_FILE_DEF(segment_info);
|
||||
F2FS_PROC_FILE_DEF(segment_bits);
|
||||
F2FS_PROC_FILE_DEF(iostat_info);
|
||||
|
||||
int __init f2fs_init_sysfs(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
kobject_set_name(&f2fs_kset.kobj, "f2fs");
|
||||
f2fs_kset.kobj.parent = fs_kobj;
|
||||
ret = kset_register(&f2fs_kset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype,
|
||||
NULL, "features");
|
||||
if (ret)
|
||||
kset_unregister(&f2fs_kset);
|
||||
else
|
||||
f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void f2fs_exit_sysfs(void)
|
||||
{
|
||||
kobject_put(&f2fs_feat);
|
||||
kset_unregister(&f2fs_kset);
|
||||
remove_proc_entry("fs/f2fs", NULL);
|
||||
f2fs_proc_root = NULL;
|
||||
}
|
||||
|
||||
int f2fs_register_sysfs(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
int err;
|
||||
|
||||
sbi->s_kobj.kset = &f2fs_kset;
|
||||
init_completion(&sbi->s_kobj_unregister);
|
||||
err = kobject_init_and_add(&sbi->s_kobj, &f2fs_sb_ktype, NULL,
|
||||
"%s", sb->s_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (f2fs_proc_root)
|
||||
sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root);
|
||||
|
||||
if (sbi->s_proc) {
|
||||
proc_create_data("segment_info", S_IRUGO, sbi->s_proc,
|
||||
&f2fs_seq_segment_info_fops, sb);
|
||||
proc_create_data("segment_bits", S_IRUGO, sbi->s_proc,
|
||||
&f2fs_seq_segment_bits_fops, sb);
|
||||
proc_create_data("iostat_info", S_IRUGO, sbi->s_proc,
|
||||
&f2fs_seq_iostat_info_fops, sb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (sbi->s_proc) {
|
||||
remove_proc_entry("iostat_info", sbi->s_proc);
|
||||
remove_proc_entry("segment_info", sbi->s_proc);
|
||||
remove_proc_entry("segment_bits", sbi->s_proc);
|
||||
remove_proc_entry(sbi->sb->s_id, f2fs_proc_root);
|
||||
}
|
||||
kobject_del(&sbi->s_kobj);
|
||||
}
|
||||
+2
-2
@@ -59,7 +59,7 @@ void f2fs_trace_pid(struct page *page)
|
||||
pid_t pid = task_pid_nr(current);
|
||||
void *p;
|
||||
|
||||
page->private = pid;
|
||||
set_page_private(page, (unsigned long)pid);
|
||||
|
||||
if (radix_tree_preload(GFP_NOFS))
|
||||
return;
|
||||
@@ -138,7 +138,7 @@ static unsigned int gang_lookup_pids(pid_t *results, unsigned long first_index,
|
||||
|
||||
radix_tree_for_each_slot(slot, &pids, &iter, first_index) {
|
||||
results[ret] = iter.index;
|
||||
if (++ret == PIDVEC_SIZE)
|
||||
if (++ret == max_items)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
|
||||
+148
-34
@@ -106,7 +106,7 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
|
||||
return -EINVAL;
|
||||
|
||||
F2FS_I(inode)->i_advise |= *(char *)value;
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -217,18 +217,123 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
|
||||
return entry;
|
||||
}
|
||||
|
||||
static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr,
|
||||
void **last_addr, int index,
|
||||
size_t len, const char *name)
|
||||
{
|
||||
struct f2fs_xattr_entry *entry;
|
||||
unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2;
|
||||
|
||||
list_for_each_xattr(entry, base_addr) {
|
||||
if ((void *)entry + sizeof(__u32) > base_addr + inline_size ||
|
||||
(void *)XATTR_NEXT_ENTRY(entry) + sizeof(__u32) >
|
||||
base_addr + inline_size) {
|
||||
*last_addr = entry;
|
||||
return NULL;
|
||||
}
|
||||
if (entry->e_name_index != index)
|
||||
continue;
|
||||
if (entry->e_name_len != len)
|
||||
continue;
|
||||
if (!memcmp(entry->e_name, name, len))
|
||||
break;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
|
||||
unsigned int index, unsigned int len,
|
||||
const char *name, struct f2fs_xattr_entry **xe,
|
||||
void **base_addr)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
void *cur_addr, *txattr_addr, *last_addr = NULL;
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0;
|
||||
unsigned int inline_size = inline_xattr_size(inode);
|
||||
int err = 0;
|
||||
|
||||
if (!size && !inline_size)
|
||||
return -ENODATA;
|
||||
|
||||
txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE,
|
||||
GFP_F2FS_ZERO);
|
||||
if (!txattr_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* read from inline xattr */
|
||||
if (inline_size) {
|
||||
struct page *page = NULL;
|
||||
void *inline_addr;
|
||||
|
||||
if (ipage) {
|
||||
inline_addr = inline_xattr_addr(ipage);
|
||||
} else {
|
||||
page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto out;
|
||||
}
|
||||
inline_addr = inline_xattr_addr(page);
|
||||
}
|
||||
memcpy(txattr_addr, inline_addr, inline_size);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
*xe = __find_inline_xattr(txattr_addr, &last_addr,
|
||||
index, len, name);
|
||||
if (*xe)
|
||||
goto check;
|
||||
}
|
||||
|
||||
/* read from xattr node block */
|
||||
if (xnid) {
|
||||
struct page *xpage;
|
||||
void *xattr_addr;
|
||||
|
||||
/* The inode already has an extended attribute block. */
|
||||
xpage = get_node_page(sbi, xnid);
|
||||
if (IS_ERR(xpage)) {
|
||||
err = PTR_ERR(xpage);
|
||||
goto out;
|
||||
}
|
||||
|
||||
xattr_addr = page_address(xpage);
|
||||
memcpy(txattr_addr + inline_size, xattr_addr, size);
|
||||
f2fs_put_page(xpage, 1);
|
||||
}
|
||||
|
||||
if (last_addr)
|
||||
cur_addr = XATTR_HDR(last_addr) - 1;
|
||||
else
|
||||
cur_addr = txattr_addr;
|
||||
|
||||
*xe = __find_xattr(cur_addr, index, len, name);
|
||||
check:
|
||||
if (IS_XATTR_LAST_ENTRY(*xe)) {
|
||||
err = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*base_addr = txattr_addr;
|
||||
return 0;
|
||||
out:
|
||||
kzfree(txattr_addr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int read_all_xattrs(struct inode *inode, struct page *ipage,
|
||||
void **base_addr)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_xattr_header *header;
|
||||
size_t size = PAGE_SIZE, inline_size = 0;
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
unsigned int size = VALID_XATTR_BLOCK_SIZE;
|
||||
unsigned int inline_size = inline_xattr_size(inode);
|
||||
void *txattr_addr;
|
||||
int err;
|
||||
|
||||
inline_size = inline_xattr_size(inode);
|
||||
|
||||
txattr_addr = kzalloc(inline_size + size, GFP_F2FS_ZERO);
|
||||
txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE,
|
||||
GFP_F2FS_ZERO);
|
||||
if (!txattr_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -252,19 +357,19 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage,
|
||||
}
|
||||
|
||||
/* read from xattr node block */
|
||||
if (F2FS_I(inode)->i_xattr_nid) {
|
||||
if (xnid) {
|
||||
struct page *xpage;
|
||||
void *xattr_addr;
|
||||
|
||||
/* The inode already has an extended attribute block. */
|
||||
xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid);
|
||||
xpage = get_node_page(sbi, xnid);
|
||||
if (IS_ERR(xpage)) {
|
||||
err = PTR_ERR(xpage);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xattr_addr = page_address(xpage);
|
||||
memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE);
|
||||
memcpy(txattr_addr + inline_size, xattr_addr, size);
|
||||
f2fs_put_page(xpage, 1);
|
||||
}
|
||||
|
||||
@@ -286,14 +391,12 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||
void *txattr_addr, struct page *ipage)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
size_t inline_size = 0;
|
||||
size_t inline_size = inline_xattr_size(inode);
|
||||
void *xattr_addr;
|
||||
struct page *xpage;
|
||||
nid_t new_nid = 0;
|
||||
int err;
|
||||
|
||||
inline_size = inline_xattr_size(inode);
|
||||
|
||||
if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid)
|
||||
if (!alloc_nid(sbi, &new_nid))
|
||||
return -ENOSPC;
|
||||
@@ -339,7 +442,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||
} else {
|
||||
struct dnode_of_data dn;
|
||||
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
||||
xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage);
|
||||
xpage = new_node_page(&dn, XATTR_NODE_OFFSET);
|
||||
if (IS_ERR(xpage)) {
|
||||
alloc_nid_failed(sbi, new_nid);
|
||||
return PTR_ERR(xpage);
|
||||
@@ -348,23 +451,20 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||
}
|
||||
|
||||
xattr_addr = page_address(xpage);
|
||||
memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE -
|
||||
sizeof(struct node_footer));
|
||||
memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE);
|
||||
set_page_dirty(xpage);
|
||||
f2fs_put_page(xpage, 1);
|
||||
|
||||
/* need to checkpoint during fsync */
|
||||
F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
||||
void *buffer, size_t buffer_size, struct page *ipage)
|
||||
{
|
||||
struct f2fs_xattr_entry *entry;
|
||||
void *base_addr;
|
||||
struct f2fs_xattr_entry *entry = NULL;
|
||||
int error = 0;
|
||||
size_t size, len;
|
||||
unsigned int size, len;
|
||||
void *base_addr = NULL;
|
||||
|
||||
if (name == NULL)
|
||||
return -EINVAL;
|
||||
@@ -373,21 +473,18 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
||||
if (len > F2FS_NAME_LEN)
|
||||
return -ERANGE;
|
||||
|
||||
error = read_all_xattrs(inode, ipage, &base_addr);
|
||||
down_read(&F2FS_I(inode)->i_xattr_sem);
|
||||
error = lookup_all_xattrs(inode, ipage, index, len, name,
|
||||
&entry, &base_addr);
|
||||
up_read(&F2FS_I(inode)->i_xattr_sem);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
entry = __find_xattr(base_addr, index, len, name);
|
||||
if (IS_XATTR_LAST_ENTRY(entry)) {
|
||||
error = -ENODATA;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
size = le16_to_cpu(entry->e_value_size);
|
||||
|
||||
if (buffer && size > buffer_size) {
|
||||
error = -ERANGE;
|
||||
goto cleanup;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
@@ -395,8 +492,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
||||
memcpy(buffer, pval, size);
|
||||
}
|
||||
error = size;
|
||||
|
||||
cleanup:
|
||||
out:
|
||||
kzfree(base_addr);
|
||||
return error;
|
||||
}
|
||||
@@ -409,7 +505,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
int error = 0;
|
||||
size_t rest = buffer_size;
|
||||
|
||||
down_read(&F2FS_I(inode)->i_xattr_sem);
|
||||
error = read_all_xattrs(inode, NULL, &base_addr);
|
||||
up_read(&F2FS_I(inode)->i_xattr_sem);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -445,6 +543,15 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool f2fs_xattr_value_same(struct f2fs_xattr_entry *entry,
|
||||
const void *value, size_t size)
|
||||
{
|
||||
void *pval = entry->e_name + entry->e_name_len;
|
||||
|
||||
return (le16_to_cpu(entry->e_value_size) == size) &&
|
||||
!memcmp(pval, value, size);
|
||||
}
|
||||
|
||||
static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
const char *name, const void *value, size_t size,
|
||||
struct page *ipage, int flags)
|
||||
@@ -479,12 +586,17 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
|
||||
found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
|
||||
|
||||
if ((flags & XATTR_REPLACE) && !found) {
|
||||
if (found) {
|
||||
if ((flags & XATTR_CREATE)) {
|
||||
error = -EEXIST;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (f2fs_xattr_value_same(here, value, size))
|
||||
goto exit;
|
||||
} else if ((flags & XATTR_REPLACE)) {
|
||||
error = -ENODATA;
|
||||
goto exit;
|
||||
} else if ((flags & XATTR_CREATE) && found) {
|
||||
error = -EEXIST;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
last = here;
|
||||
@@ -554,7 +666,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
|
||||
!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
if (!error && S_ISDIR(inode->i_mode))
|
||||
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP);
|
||||
exit:
|
||||
@@ -578,7 +690,9 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
|
||||
f2fs_lock_op(sbi);
|
||||
/* protect xattr_ver */
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
down_write(&F2FS_I(inode)->i_xattr_sem);
|
||||
err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
|
||||
up_write(&F2FS_I(inode)->i_xattr_sem);
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
|
||||
+6
-5
@@ -58,10 +58,10 @@ struct f2fs_xattr_entry {
|
||||
#define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr) + 1))
|
||||
#define XATTR_ROUND (3)
|
||||
|
||||
#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND)
|
||||
#define XATTR_ALIGN(size) (((size) + XATTR_ROUND) & ~XATTR_ROUND)
|
||||
|
||||
#define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \
|
||||
entry->e_name_len + le16_to_cpu(entry->e_value_size)))
|
||||
(entry)->e_name_len + le16_to_cpu((entry)->e_value_size)))
|
||||
|
||||
#define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *)((char *)(entry) +\
|
||||
ENTRY_SIZE(entry)))
|
||||
@@ -72,9 +72,10 @@ struct f2fs_xattr_entry {
|
||||
for (entry = XATTR_FIRST_ENTRY(addr);\
|
||||
!IS_XATTR_LAST_ENTRY(entry);\
|
||||
entry = XATTR_NEXT_ENTRY(entry))
|
||||
|
||||
#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + PAGE_SIZE - \
|
||||
sizeof(struct node_footer) - sizeof(__u32))
|
||||
#define VALID_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer))
|
||||
#define XATTR_PADDING_SIZE (sizeof(__u32))
|
||||
#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \
|
||||
VALID_XATTR_BLOCK_SIZE)
|
||||
|
||||
#define MAX_VALUE_LEN(i) (MIN_OFFSET(i) - \
|
||||
sizeof(struct f2fs_xattr_header) - \
|
||||
|
||||
@@ -329,7 +329,7 @@ static void fscache_objlist_config(struct fscache_objlist_data *data)
|
||||
config = 0;
|
||||
rcu_read_lock();
|
||||
|
||||
confkey = user_key_payload(key);
|
||||
confkey = user_key_payload_rcu(key);
|
||||
buf = confkey->data;
|
||||
|
||||
for (len = confkey->datalen - 1; len >= 0; len--) {
|
||||
|
||||
+1
-1
@@ -316,7 +316,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
|
||||
if (ret < 0)
|
||||
goto out_up;
|
||||
|
||||
payload = user_key_payload(rkey);
|
||||
payload = user_key_payload_rcu(rkey);
|
||||
if (IS_ERR_OR_NULL(payload)) {
|
||||
ret = PTR_ERR(payload);
|
||||
goto out_up;
|
||||
|
||||
@@ -48,9 +48,14 @@ extern void user_describe(const struct key *user, struct seq_file *m);
|
||||
extern long user_read(const struct key *key,
|
||||
char __user *buffer, size_t buflen);
|
||||
|
||||
static inline const struct user_key_payload *user_key_payload(const struct key *key)
|
||||
static inline const struct user_key_payload *user_key_payload_rcu(const struct key *key)
|
||||
{
|
||||
return (struct user_key_payload *)rcu_dereference_key(key);
|
||||
return (struct user_key_payload *)dereference_key_rcu(key);
|
||||
}
|
||||
|
||||
static inline struct user_key_payload *user_key_payload_locked(const struct key *key)
|
||||
{
|
||||
return (struct user_key_payload *)dereference_key_locked((struct key *)key);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
+39
-30
@@ -32,9 +32,15 @@
|
||||
/* 0, 1(node nid), 2(meta nid) are reserved node id */
|
||||
#define F2FS_RESERVED_NODE_NUM 3
|
||||
|
||||
#define F2FS_ROOT_INO(sbi) (sbi->root_ino_num)
|
||||
#define F2FS_NODE_INO(sbi) (sbi->node_ino_num)
|
||||
#define F2FS_META_INO(sbi) (sbi->meta_ino_num)
|
||||
#define F2FS_ROOT_INO(sbi) ((sbi)->root_ino_num)
|
||||
#define F2FS_NODE_INO(sbi) ((sbi)->node_ino_num)
|
||||
#define F2FS_META_INO(sbi) ((sbi)->meta_ino_num)
|
||||
|
||||
#define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */
|
||||
#define F2FS_IO_SIZE_KB(sbi) (1 << ((sbi)->write_io_size_bits + 2)) /* KB */
|
||||
#define F2FS_IO_SIZE_BYTES(sbi) (1 << ((sbi)->write_io_size_bits + 12)) /* B */
|
||||
#define F2FS_IO_SIZE_BITS(sbi) ((sbi)->write_io_size_bits) /* power of 2 */
|
||||
#define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1)
|
||||
|
||||
/* This flag is used by node and meta inodes, and by recovery */
|
||||
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
|
||||
@@ -52,10 +58,17 @@
|
||||
|
||||
#define VERSION_LEN 256
|
||||
#define MAX_VOLUME_NAME 512
|
||||
#define MAX_PATH_LEN 64
|
||||
#define MAX_DEVICES 8
|
||||
|
||||
/*
|
||||
* For superblock
|
||||
*/
|
||||
struct f2fs_device {
|
||||
__u8 path[MAX_PATH_LEN];
|
||||
__le32 total_segments;
|
||||
} __packed;
|
||||
|
||||
struct f2fs_super_block {
|
||||
__le32 magic; /* Magic Number */
|
||||
__le16 major_ver; /* Major Version */
|
||||
@@ -94,12 +107,15 @@ struct f2fs_super_block {
|
||||
__le32 feature; /* defined features */
|
||||
__u8 encryption_level; /* versioning level for encryption */
|
||||
__u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
|
||||
__u8 reserved[871]; /* valid reserved region */
|
||||
struct f2fs_device devs[MAX_DEVICES]; /* device list */
|
||||
__u8 reserved[327]; /* valid reserved region */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* For checkpoint
|
||||
*/
|
||||
#define CP_TRIMMED_FLAG 0x00000100
|
||||
#define CP_NAT_BITS_FLAG 0x00000080
|
||||
#define CP_CRC_RECOVERY_FLAG 0x00000040
|
||||
#define CP_FASTBOOT_FLAG 0x00000020
|
||||
#define CP_FSCK_FLAG 0x00000010
|
||||
@@ -146,7 +162,7 @@ struct f2fs_checkpoint {
|
||||
*/
|
||||
#define F2FS_ORPHANS_PER_BLOCK 1020
|
||||
|
||||
#define GET_ORPHAN_BLOCKS(n) ((n + F2FS_ORPHANS_PER_BLOCK - 1) / \
|
||||
#define GET_ORPHAN_BLOCKS(n) (((n) + F2FS_ORPHANS_PER_BLOCK - 1) / \
|
||||
F2FS_ORPHANS_PER_BLOCK)
|
||||
|
||||
struct f2fs_orphan_block {
|
||||
@@ -170,6 +186,8 @@ struct f2fs_extent {
|
||||
#define F2FS_NAME_LEN 255
|
||||
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
|
||||
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
||||
#define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \
|
||||
get_extra_isize(inode))
|
||||
#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */
|
||||
#define ADDRS_PER_INODE(inode) addrs_per_inode(inode)
|
||||
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||
@@ -189,9 +207,7 @@ struct f2fs_extent {
|
||||
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
|
||||
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
|
||||
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
|
||||
|
||||
#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
|
||||
F2FS_INLINE_XATTR_ADDRS - 1))
|
||||
#define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */
|
||||
|
||||
struct f2fs_inode {
|
||||
__le16 i_mode; /* file mode */
|
||||
@@ -219,8 +235,16 @@ struct f2fs_inode {
|
||||
|
||||
struct f2fs_extent i_ext; /* caching a largest extent */
|
||||
|
||||
__le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
|
||||
|
||||
union {
|
||||
struct {
|
||||
__le16 i_extra_isize; /* extra inode attribute size */
|
||||
__le16 i_padding; /* padding */
|
||||
__le32 i_projid; /* project id */
|
||||
__le32 i_inode_checksum;/* inode meta checksum */
|
||||
__le32 i_extra_end[0]; /* for attribute size calculation */
|
||||
};
|
||||
__le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
|
||||
};
|
||||
__le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2),
|
||||
double_indirect(1) node id */
|
||||
} __packed;
|
||||
@@ -264,6 +288,7 @@ struct f2fs_node {
|
||||
* For NAT entries
|
||||
*/
|
||||
#define NAT_ENTRY_PER_BLOCK (PAGE_SIZE / sizeof(struct f2fs_nat_entry))
|
||||
#define NAT_ENTRY_BITMAP_SIZE ((NAT_ENTRY_PER_BLOCK + 7) / 8)
|
||||
|
||||
struct f2fs_nat_entry {
|
||||
__u8 version; /* latest version of cached nat entry */
|
||||
@@ -439,7 +464,7 @@ typedef __le32 f2fs_hash_t;
|
||||
#define F2FS_SLOT_LEN 8
|
||||
#define F2FS_SLOT_LEN_BITS 3
|
||||
|
||||
#define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS)
|
||||
#define GET_DENTRY_SLOTS(x) (((x) + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS)
|
||||
|
||||
/* MAX level for dir lookup */
|
||||
#define MAX_DIR_HASH_DEPTH 63
|
||||
@@ -448,7 +473,7 @@ typedef __le32 f2fs_hash_t;
|
||||
#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1))
|
||||
|
||||
/*
|
||||
* space utilization of regular dentry and inline dentry
|
||||
* space utilization of regular dentry and inline dentry (w/o extra reservation)
|
||||
* regular dentry inline dentry
|
||||
* bitmap 1 * 27 = 27 1 * 23 = 23
|
||||
* reserved 1 * 3 = 3 1 * 7 = 7
|
||||
@@ -484,24 +509,6 @@ struct f2fs_dentry_block {
|
||||
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
|
||||
} __packed;
|
||||
|
||||
/* for inline dir */
|
||||
#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \
|
||||
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
|
||||
BITS_PER_BYTE + 1))
|
||||
#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \
|
||||
BITS_PER_BYTE - 1) / BITS_PER_BYTE)
|
||||
#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \
|
||||
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
|
||||
NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE))
|
||||
|
||||
/* inline directory entry structure */
|
||||
struct f2fs_inline_dentry {
|
||||
__u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE];
|
||||
__u8 reserved[INLINE_RESERVED_SIZE];
|
||||
struct f2fs_dir_entry dentry[NR_INLINE_DENTRY];
|
||||
__u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN];
|
||||
} __packed;
|
||||
|
||||
/* file types used in inode_info->flags */
|
||||
enum {
|
||||
F2FS_FT_UNKNOWN,
|
||||
@@ -517,4 +524,6 @@ enum {
|
||||
|
||||
#define S_SHIFT 12
|
||||
|
||||
#define F2FS_DEF_PROJID 0 /* default project ID */
|
||||
|
||||
#endif /* _LINUX_F2FS_FS_H */
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* fscrypt_common.h: common declarations for per-file encryption
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FSCRYPT_COMMON_H
|
||||
#define _LINUX_FSCRYPT_COMMON_H
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <uapi/linux/fs.h>
|
||||
|
||||
#define FS_CRYPTO_BLOCK_SIZE 16
|
||||
|
||||
struct fscrypt_info;
|
||||
|
||||
struct fscrypt_ctx {
|
||||
union {
|
||||
struct {
|
||||
struct page *bounce_page; /* Ciphertext page */
|
||||
struct page *control_page; /* Original page */
|
||||
} w;
|
||||
struct {
|
||||
struct bio *bio;
|
||||
struct work_struct work;
|
||||
} r;
|
||||
struct list_head free_list; /* Free list */
|
||||
};
|
||||
u8 flags; /* Flags */
|
||||
};
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
*/
|
||||
struct fscrypt_symlink_data {
|
||||
__le16 len;
|
||||
char encrypted_path[1];
|
||||
} __packed;
|
||||
|
||||
struct fscrypt_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct fscrypt_name {
|
||||
const struct qstr *usr_fname;
|
||||
struct fscrypt_str disk_name;
|
||||
u32 hash;
|
||||
u32 minor_hash;
|
||||
struct fscrypt_str crypto_buf;
|
||||
};
|
||||
|
||||
#define FSTR_INIT(n, l) { .name = n, .len = l }
|
||||
#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len)
|
||||
#define fname_name(p) ((p)->disk_name.name)
|
||||
#define fname_len(p) ((p)->disk_name.len)
|
||||
|
||||
/*
|
||||
* fscrypt superblock flags
|
||||
*/
|
||||
#define FS_CFLG_OWN_PAGES (1U << 1)
|
||||
|
||||
/*
|
||||
* crypto opertions for filesystems
|
||||
*/
|
||||
struct fscrypt_operations {
|
||||
unsigned int flags;
|
||||
const char *key_prefix;
|
||||
int (*get_context)(struct inode *, void *, size_t);
|
||||
int (*set_context)(struct inode *, const void *, size_t, void *);
|
||||
int (*dummy_context)(struct inode *);
|
||||
bool (*is_encrypted)(struct inode *);
|
||||
bool (*empty_dir)(struct inode *);
|
||||
unsigned (*max_namelen)(struct inode *);
|
||||
};
|
||||
|
||||
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
||||
{
|
||||
if (inode->i_sb->s_cop->dummy_context &&
|
||||
inode->i_sb->s_cop->dummy_context(inode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
|
||||
u32 filenames_mode)
|
||||
{
|
||||
if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC &&
|
||||
filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS)
|
||||
return true;
|
||||
|
||||
if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
|
||||
filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
||||
{
|
||||
if (str->len == 1 && str->name[0] == '.')
|
||||
return true;
|
||||
|
||||
if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_control_page(struct page *page)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
return ((struct fscrypt_ctx *)page_private(page))->w.control_page;
|
||||
#else
|
||||
WARN_ON_ONCE(1);
|
||||
return ERR_PTR(-EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int fscrypt_has_encryption_key(const struct inode *inode)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
return (inode->i_crypt_info != NULL);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _LINUX_FSCRYPT_COMMON_H */
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* fscrypt_notsupp.h
|
||||
*
|
||||
* This stubs out the fscrypt functions for filesystems configured without
|
||||
* encryption support.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FSCRYPT_NOTSUPP_H
|
||||
#define _LINUX_FSCRYPT_NOTSUPP_H
|
||||
|
||||
#include <linux/fscrypt_common.h>
|
||||
|
||||
/* crypto.c */
|
||||
static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_encrypt_page(const struct inode *inode,
|
||||
struct page *page,
|
||||
unsigned int len,
|
||||
unsigned int offs,
|
||||
u64 lblk_num, gfp_t gfp_flags)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline int fscrypt_decrypt_page(const struct inode *inode,
|
||||
struct page *page,
|
||||
unsigned int len, unsigned int offs,
|
||||
u64 lblk_num)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
||||
static inline void fscrypt_restore_control_page(struct page *page)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_set_d_op(struct dentry *dentry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* policy.c */
|
||||
static inline int fscrypt_ioctl_set_policy(struct file *filp,
|
||||
const void __user *arg)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_has_permitted_context(struct inode *parent,
|
||||
struct inode *child)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_inherit_context(struct inode *parent,
|
||||
struct inode *child,
|
||||
void *fs_data, bool preload)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* keyinfo.c */
|
||||
static inline int fscrypt_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_put_encryption_info(struct inode *inode,
|
||||
struct fscrypt_info *ci)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* fname.c */
|
||||
static inline int fscrypt_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
if (dir->i_sb->s_cop->is_encrypted(dir))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void fscrypt_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline u32 fscrypt_fname_encrypted_size(const struct inode *inode,
|
||||
u32 ilen)
|
||||
{
|
||||
/* never happens */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_fname_alloc_buffer(const struct inode *inode,
|
||||
u32 ilen,
|
||||
struct fscrypt_str *crypto_str)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
/* Encryption support disabled; use standard comparison */
|
||||
if (de_name_len != fname->disk_name.len)
|
||||
return false;
|
||||
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
|
||||
}
|
||||
|
||||
/* bio.c */
|
||||
static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx,
|
||||
struct bio *bio)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_pullback_bio_page(struct page **page, bool restore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif /* _LINUX_FSCRYPT_NOTSUPP_H */
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* fscrypt_supp.h
|
||||
*
|
||||
* This is included by filesystems configured with encryption support.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FSCRYPT_SUPP_H
|
||||
#define _LINUX_FSCRYPT_SUPP_H
|
||||
|
||||
#include <linux/fscrypt_common.h>
|
||||
|
||||
/* crypto.c */
|
||||
extern struct kmem_cache *fscrypt_info_cachep;
|
||||
extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t);
|
||||
extern void fscrypt_release_ctx(struct fscrypt_ctx *);
|
||||
extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *,
|
||||
unsigned int, unsigned int,
|
||||
u64, gfp_t);
|
||||
extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int,
|
||||
unsigned int, u64);
|
||||
extern void fscrypt_restore_control_page(struct page *);
|
||||
|
||||
extern const struct dentry_operations fscrypt_d_ops;
|
||||
|
||||
static inline void fscrypt_set_d_op(struct dentry *dentry)
|
||||
{
|
||||
d_set_d_op(dentry, &fscrypt_d_ops);
|
||||
}
|
||||
|
||||
static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
/* policy.c */
|
||||
extern int fscrypt_ioctl_set_policy(struct file *, const void __user *);
|
||||
extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
|
||||
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
|
||||
extern int fscrypt_inherit_context(struct inode *, struct inode *,
|
||||
void *, bool);
|
||||
/* keyinfo.c */
|
||||
extern int fscrypt_get_encryption_info(struct inode *);
|
||||
extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *);
|
||||
|
||||
/* fname.c */
|
||||
extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
|
||||
int lookup, struct fscrypt_name *);
|
||||
|
||||
static inline void fscrypt_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
kfree(fname->crypto_buf.name);
|
||||
}
|
||||
|
||||
extern u32 fscrypt_fname_encrypted_size(const struct inode *, u32);
|
||||
extern int fscrypt_fname_alloc_buffer(const struct inode *, u32,
|
||||
struct fscrypt_str *);
|
||||
extern void fscrypt_fname_free_buffer(struct fscrypt_str *);
|
||||
extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32,
|
||||
const struct fscrypt_str *, struct fscrypt_str *);
|
||||
extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
|
||||
struct fscrypt_str *);
|
||||
|
||||
#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32
|
||||
|
||||
/* Extracts the second-to-last ciphertext block; see explanation below */
|
||||
#define FSCRYPT_FNAME_DIGEST(name, len) \
|
||||
((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \
|
||||
FS_CRYPTO_BLOCK_SIZE))
|
||||
|
||||
#define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE
|
||||
|
||||
/**
|
||||
* fscrypt_digested_name - alternate identifier for an on-disk filename
|
||||
*
|
||||
* When userspace lists an encrypted directory without access to the key,
|
||||
* filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE
|
||||
* bytes are shown in this abbreviated form (base64-encoded) rather than as the
|
||||
* full ciphertext (base64-encoded). This is necessary to allow supporting
|
||||
* filenames up to NAME_MAX bytes, since base64 encoding expands the length.
|
||||
*
|
||||
* To make it possible for filesystems to still find the correct directory entry
|
||||
* despite not knowing the full on-disk name, we encode any filesystem-specific
|
||||
* 'hash' and/or 'minor_hash' which the filesystem may need for its lookups,
|
||||
* followed by the second-to-last ciphertext block of the filename. Due to the
|
||||
* use of the CBC-CTS encryption mode, the second-to-last ciphertext block
|
||||
* depends on the full plaintext. (Note that ciphertext stealing causes the
|
||||
* last two blocks to appear "flipped".) This makes accidental collisions very
|
||||
* unlikely: just a 1 in 2^128 chance for two filenames to collide even if they
|
||||
* share the same filesystem-specific hashes.
|
||||
*
|
||||
* However, this scheme isn't immune to intentional collisions, which can be
|
||||
* created by anyone able to create arbitrary plaintext filenames and view them
|
||||
* without the key. Making the "digest" be a real cryptographic hash like
|
||||
* SHA-256 over the full ciphertext would prevent this, although it would be
|
||||
* less efficient and harder to implement, especially since the filesystem would
|
||||
* need to calculate it for each directory entry examined during a search.
|
||||
*/
|
||||
struct fscrypt_digested_name {
|
||||
u32 hash;
|
||||
u32 minor_hash;
|
||||
u8 digest[FSCRYPT_FNAME_DIGEST_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* fscrypt_match_name() - test whether the given name matches a directory entry
|
||||
* @fname: the name being searched for
|
||||
* @de_name: the name from the directory entry
|
||||
* @de_name_len: the length of @de_name in bytes
|
||||
*
|
||||
* Normally @fname->disk_name will be set, and in that case we simply compare
|
||||
* that to the name stored in the directory entry. The only exception is that
|
||||
* if we don't have the key for an encrypted directory and a filename in it is
|
||||
* very long, then we won't have the full disk_name and we'll instead need to
|
||||
* match against the fscrypt_digested_name.
|
||||
*
|
||||
* Return: %true if the name matches, otherwise %false.
|
||||
*/
|
||||
static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
if (unlikely(!fname->disk_name.name)) {
|
||||
const struct fscrypt_digested_name *n =
|
||||
(const void *)fname->crypto_buf.name;
|
||||
if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_'))
|
||||
return false;
|
||||
if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)
|
||||
return false;
|
||||
return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len),
|
||||
n->digest, FSCRYPT_FNAME_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
if (de_name_len != fname->disk_name.len)
|
||||
return false;
|
||||
return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
|
||||
}
|
||||
|
||||
/* bio.c */
|
||||
extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
|
||||
extern void fscrypt_pullback_bio_page(struct page **, bool);
|
||||
extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
|
||||
unsigned int);
|
||||
|
||||
#endif /* _LINUX_FSCRYPT_SUPP_H */
|
||||
@@ -1,409 +0,0 @@
|
||||
/*
|
||||
* General per-file encryption definition
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FSCRYPTO_H
|
||||
#define _LINUX_FSCRYPTO_H
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <uapi/linux/fs.h>
|
||||
|
||||
#define FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
|
||||
#define FS_POLICY_FLAGS_PAD_4 0x00
|
||||
#define FS_POLICY_FLAGS_PAD_8 0x01
|
||||
#define FS_POLICY_FLAGS_PAD_16 0x02
|
||||
#define FS_POLICY_FLAGS_PAD_32 0x03
|
||||
#define FS_POLICY_FLAGS_PAD_MASK 0x03
|
||||
#define FS_POLICY_FLAGS_VALID 0x03
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define FS_ENCRYPTION_MODE_INVALID 0
|
||||
#define FS_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define FS_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define FS_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define FS_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
|
||||
/**
|
||||
* Encryption context for inode
|
||||
*
|
||||
* Protector format:
|
||||
* 1 byte: Protector format (1 = this version)
|
||||
* 1 byte: File contents encryption mode
|
||||
* 1 byte: File names encryption mode
|
||||
* 1 byte: Flags
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct fscrypt_context {
|
||||
u8 format;
|
||||
u8 contents_encryption_mode;
|
||||
u8 filenames_encryption_mode;
|
||||
u8 flags;
|
||||
u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __packed;
|
||||
|
||||
/* Encryption parameters */
|
||||
#define FS_XTS_TWEAK_SIZE 16
|
||||
#define FS_AES_128_ECB_KEY_SIZE 16
|
||||
#define FS_AES_256_GCM_KEY_SIZE 32
|
||||
#define FS_AES_256_CBC_KEY_SIZE 32
|
||||
#define FS_AES_256_CTS_KEY_SIZE 32
|
||||
#define FS_AES_256_XTS_KEY_SIZE 64
|
||||
#define FS_MAX_KEY_SIZE 64
|
||||
|
||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||
|
||||
/* This is passed in from userspace into the kernel keyring */
|
||||
struct fscrypt_key {
|
||||
u32 mode;
|
||||
u8 raw[FS_MAX_KEY_SIZE];
|
||||
u32 size;
|
||||
} __packed;
|
||||
|
||||
struct fscrypt_info {
|
||||
u8 ci_data_mode;
|
||||
u8 ci_filename_mode;
|
||||
u8 ci_flags;
|
||||
struct crypto_skcipher *ci_ctfm;
|
||||
u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
|
||||
};
|
||||
|
||||
#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define FS_WRITE_PATH_FL 0x00000002
|
||||
|
||||
struct fscrypt_ctx {
|
||||
union {
|
||||
struct {
|
||||
struct page *bounce_page; /* Ciphertext page */
|
||||
struct page *control_page; /* Original page */
|
||||
} w;
|
||||
struct {
|
||||
struct bio *bio;
|
||||
struct work_struct work;
|
||||
} r;
|
||||
struct list_head free_list; /* Free list */
|
||||
};
|
||||
u8 flags; /* Flags */
|
||||
u8 mode; /* Encryption mode for tfm */
|
||||
};
|
||||
|
||||
struct fscrypt_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_FS_COMPLETION_RESULT(ecr) \
|
||||
struct fscrypt_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER((ecr).completion), 0 }
|
||||
|
||||
#define FS_FNAME_NUM_SCATTER_ENTRIES 4
|
||||
#define FS_CRYPTO_BLOCK_SIZE 16
|
||||
#define FS_FNAME_CRYPTO_DIGEST_SIZE 32
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
*/
|
||||
struct fscrypt_symlink_data {
|
||||
__le16 len;
|
||||
char encrypted_path[1];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* This function is used to calculate the disk space required to
|
||||
* store a filename of length l in encrypted symlink format.
|
||||
*/
|
||||
static inline u32 fscrypt_symlink_data_len(u32 l)
|
||||
{
|
||||
if (l < FS_CRYPTO_BLOCK_SIZE)
|
||||
l = FS_CRYPTO_BLOCK_SIZE;
|
||||
return (l + sizeof(struct fscrypt_symlink_data) - 1);
|
||||
}
|
||||
|
||||
struct fscrypt_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct fscrypt_name {
|
||||
const struct qstr *usr_fname;
|
||||
struct fscrypt_str disk_name;
|
||||
u32 hash;
|
||||
u32 minor_hash;
|
||||
struct fscrypt_str crypto_buf;
|
||||
};
|
||||
|
||||
#define FSTR_INIT(n, l) { .name = n, .len = l }
|
||||
#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len)
|
||||
#define fname_name(p) ((p)->disk_name.name)
|
||||
#define fname_len(p) ((p)->disk_name.len)
|
||||
|
||||
/*
|
||||
* crypto opertions for filesystems
|
||||
*/
|
||||
struct fscrypt_operations {
|
||||
int (*get_context)(struct inode *, void *, size_t);
|
||||
int (*key_prefix)(struct inode *, u8 **);
|
||||
int (*prepare_context)(struct inode *);
|
||||
int (*set_context)(struct inode *, const void *, size_t, void *);
|
||||
int (*dummy_context)(struct inode *);
|
||||
bool (*is_encrypted)(struct inode *);
|
||||
bool (*empty_dir)(struct inode *);
|
||||
unsigned (*max_namelen)(struct inode *);
|
||||
};
|
||||
|
||||
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
||||
{
|
||||
if (inode->i_sb->s_cop->dummy_context &&
|
||||
inode->i_sb->s_cop->dummy_context(inode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_contents_enc_mode(u32 mode)
|
||||
{
|
||||
return (mode == FS_ENCRYPTION_MODE_AES_256_XTS);
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_filenames_enc_mode(u32 mode)
|
||||
{
|
||||
return (mode == FS_ENCRYPTION_MODE_AES_256_CTS);
|
||||
}
|
||||
|
||||
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
||||
{
|
||||
if (str->len == 1 && str->name[0] == '.')
|
||||
return true;
|
||||
|
||||
if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_control_page(struct page *page)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
return ((struct fscrypt_ctx *)page_private(page))->w.control_page;
|
||||
#else
|
||||
WARN_ON_ONCE(1);
|
||||
return ERR_PTR(-EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int fscrypt_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
return (inode->i_crypt_info != NULL);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
extern const struct dentry_operations fscrypt_d_ops;
|
||||
#endif
|
||||
|
||||
static inline void fscrypt_set_d_op(struct dentry *dentry)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
d_set_d_op(dentry, &fscrypt_d_ops);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
/* crypto.c */
|
||||
extern struct kmem_cache *fscrypt_info_cachep;
|
||||
int fscrypt_initialize(void);
|
||||
|
||||
extern struct fscrypt_ctx *fscrypt_get_ctx(struct inode *, gfp_t);
|
||||
extern void fscrypt_release_ctx(struct fscrypt_ctx *);
|
||||
extern struct page *fscrypt_encrypt_page(struct inode *, struct page *, gfp_t);
|
||||
extern int fscrypt_decrypt_page(struct page *);
|
||||
extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
|
||||
extern void fscrypt_pullback_bio_page(struct page **, bool);
|
||||
extern void fscrypt_restore_control_page(struct page *);
|
||||
extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t,
|
||||
unsigned int);
|
||||
/* policy.c */
|
||||
extern int fscrypt_process_policy(struct file *, const struct fscrypt_policy *);
|
||||
extern int fscrypt_get_policy(struct inode *, struct fscrypt_policy *);
|
||||
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
|
||||
extern int fscrypt_inherit_context(struct inode *, struct inode *,
|
||||
void *, bool);
|
||||
/* keyinfo.c */
|
||||
extern int fscrypt_get_encryption_info(struct inode *);
|
||||
extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *);
|
||||
|
||||
/* fname.c */
|
||||
extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
|
||||
int lookup, struct fscrypt_name *);
|
||||
extern void fscrypt_free_filename(struct fscrypt_name *);
|
||||
extern u32 fscrypt_fname_encrypted_size(struct inode *, u32);
|
||||
extern int fscrypt_fname_alloc_buffer(struct inode *, u32,
|
||||
struct fscrypt_str *);
|
||||
extern void fscrypt_fname_free_buffer(struct fscrypt_str *);
|
||||
extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32,
|
||||
const struct fscrypt_str *, struct fscrypt_str *);
|
||||
extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
|
||||
struct fscrypt_str *);
|
||||
#endif
|
||||
|
||||
/* crypto.c */
|
||||
static inline struct fscrypt_ctx *fscrypt_notsupp_get_ctx(struct inode *i,
|
||||
gfp_t f)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_release_ctx(struct fscrypt_ctx *c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_notsupp_encrypt_page(struct inode *i,
|
||||
struct page *p, gfp_t f)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_decrypt_page(struct page *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_decrypt_bio_pages(struct fscrypt_ctx *c,
|
||||
struct bio *b)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_pullback_bio_page(struct page **p, bool b)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_restore_control_page(struct page *p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p,
|
||||
sector_t s, unsigned int f)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* policy.c */
|
||||
static inline int fscrypt_notsupp_process_policy(struct file *f,
|
||||
const struct fscrypt_policy *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_get_policy(struct inode *i,
|
||||
struct fscrypt_policy *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_has_permitted_context(struct inode *p,
|
||||
struct inode *i)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_inherit_context(struct inode *p,
|
||||
struct inode *i, void *v, bool b)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* keyinfo.c */
|
||||
static inline int fscrypt_notsupp_get_encryption_info(struct inode *i)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_put_encryption_info(struct inode *i,
|
||||
struct fscrypt_info *f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* fname.c */
|
||||
static inline int fscrypt_notsupp_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
if (dir->i_sb->s_cop->is_encrypted(dir))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline u32 fscrypt_notsupp_fname_encrypted_size(struct inode *i, u32 s)
|
||||
{
|
||||
/* never happens */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_fname_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_fname_free_buffer(struct fscrypt_str *c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_fname_disk_to_usr(struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* _LINUX_FSCRYPTO_H */
|
||||
+4
-1
@@ -356,7 +356,10 @@ static inline bool key_is_instantiated(const struct key *key)
|
||||
!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
||||
}
|
||||
|
||||
#define rcu_dereference_key(KEY) \
|
||||
#define dereference_key_rcu(KEY) \
|
||||
(rcu_dereference((KEY)->payload.rcu_data0))
|
||||
|
||||
#define dereference_key_locked(KEY) \
|
||||
(rcu_dereference_protected((KEY)->payload.rcu_data0, \
|
||||
rwsem_is_locked(&((struct key *)(KEY))->sem)))
|
||||
|
||||
|
||||
+257
-67
@@ -6,8 +6,8 @@
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#define show_dev(entry) MAJOR(entry->dev), MINOR(entry->dev)
|
||||
#define show_dev_ino(entry) show_dev(entry), (unsigned long)entry->ino
|
||||
#define show_dev(dev) MAJOR(dev), MINOR(dev)
|
||||
#define show_dev_ino(entry) show_dev(entry->dev), (unsigned long)entry->ino
|
||||
|
||||
TRACE_DEFINE_ENUM(NODE);
|
||||
TRACE_DEFINE_ENUM(DATA);
|
||||
@@ -15,6 +15,8 @@ TRACE_DEFINE_ENUM(META);
|
||||
TRACE_DEFINE_ENUM(META_FLUSH);
|
||||
TRACE_DEFINE_ENUM(INMEM);
|
||||
TRACE_DEFINE_ENUM(INMEM_DROP);
|
||||
TRACE_DEFINE_ENUM(INMEM_INVALIDATE);
|
||||
TRACE_DEFINE_ENUM(INMEM_REVOKE);
|
||||
TRACE_DEFINE_ENUM(IPU);
|
||||
TRACE_DEFINE_ENUM(OPU);
|
||||
TRACE_DEFINE_ENUM(CURSEG_HOT_DATA);
|
||||
@@ -42,6 +44,7 @@ TRACE_DEFINE_ENUM(CP_FASTBOOT);
|
||||
TRACE_DEFINE_ENUM(CP_SYNC);
|
||||
TRACE_DEFINE_ENUM(CP_RECOVERY);
|
||||
TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
TRACE_DEFINE_ENUM(CP_TRIMMED);
|
||||
|
||||
#define show_block_type(type) \
|
||||
__print_symbolic(type, \
|
||||
@@ -51,32 +54,40 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
{ META_FLUSH, "META_FLUSH" }, \
|
||||
{ INMEM, "INMEM" }, \
|
||||
{ INMEM_DROP, "INMEM_DROP" }, \
|
||||
{ INMEM_INVALIDATE, "INMEM_INVALIDATE" }, \
|
||||
{ INMEM_REVOKE, "INMEM_REVOKE" }, \
|
||||
{ IPU, "IN-PLACE" }, \
|
||||
{ OPU, "OUT-OF-PLACE" })
|
||||
|
||||
#define F2FS_BIO_FLAG_MASK(t) (t & (REQ_RAHEAD | WRITE_FLUSH_FUA))
|
||||
#define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO))
|
||||
#define F2FS_OP_FLAGS (REQ_RAHEAD | REQ_SYNC | REQ_META | REQ_PRIO | \
|
||||
REQ_PREFLUSH | REQ_FUA)
|
||||
#define F2FS_BIO_FLAG_MASK(t) (t & F2FS_OP_FLAGS)
|
||||
|
||||
#define show_bio_type(op_flags) show_bio_op_flags(op_flags), \
|
||||
show_bio_extra(op_flags)
|
||||
#define show_bio_type(op,op_flags) show_bio_op(op), \
|
||||
show_bio_op_flags(op_flags)
|
||||
|
||||
#define show_bio_op(op) \
|
||||
__print_symbolic(op, \
|
||||
{ REQ_OP_READ, "READ" }, \
|
||||
{ REQ_OP_WRITE, "WRITE" }, \
|
||||
{ REQ_OP_DISCARD, "DISCARD" }, \
|
||||
{ REQ_OP_SECURE_ERASE, "SECURE_ERASE" }, \
|
||||
{ REQ_OP_WRITE_SAME, "WRITE_SAME" })
|
||||
|
||||
#define show_bio_op_flags(flags) \
|
||||
__print_symbolic(F2FS_BIO_FLAG_MASK(flags), \
|
||||
{ 0, "WRITE" }, \
|
||||
{ REQ_RAHEAD, "READAHEAD" }, \
|
||||
{ READ_SYNC, "READ_SYNC" }, \
|
||||
{ WRITE_SYNC, "WRITE_SYNC" }, \
|
||||
{ WRITE_FLUSH, "WRITE_FLUSH" }, \
|
||||
{ WRITE_FUA, "WRITE_FUA" }, \
|
||||
{ WRITE_FLUSH_FUA, "WRITE_FLUSH_FUA" })
|
||||
__print_flags(F2FS_BIO_FLAG_MASK(flags), "|", \
|
||||
{ REQ_RAHEAD, "R" }, \
|
||||
{ REQ_SYNC, "S" }, \
|
||||
{ REQ_META, "M" }, \
|
||||
{ REQ_PRIO, "P" }, \
|
||||
{ REQ_PREFLUSH, "PF" }, \
|
||||
{ REQ_FUA, "FUA" })
|
||||
|
||||
#define show_bio_extra(type) \
|
||||
__print_symbolic(F2FS_BIO_EXTRA_MASK(type), \
|
||||
{ REQ_META, "(M)" }, \
|
||||
{ REQ_PRIO, "(P)" }, \
|
||||
{ REQ_META | REQ_PRIO, "(MP)" }, \
|
||||
{ 0, " \b" })
|
||||
#define show_block_temp(temp) \
|
||||
__print_symbolic(temp, \
|
||||
{ HOT, "HOT" }, \
|
||||
{ WARM, "WARM" }, \
|
||||
{ COLD, "COLD" })
|
||||
|
||||
#define show_data_type(type) \
|
||||
__print_symbolic(type, \
|
||||
@@ -109,12 +120,14 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
{ GC_CB, "Cost-Benefit" })
|
||||
|
||||
#define show_cpreason(type) \
|
||||
__print_symbolic(type, \
|
||||
__print_flags(type, "|", \
|
||||
{ CP_UMOUNT, "Umount" }, \
|
||||
{ CP_FASTBOOT, "Fastboot" }, \
|
||||
{ CP_SYNC, "Sync" }, \
|
||||
{ CP_RECOVERY, "Recovery" }, \
|
||||
{ CP_DISCARD, "Discard" })
|
||||
{ CP_DISCARD, "Discard" }, \
|
||||
{ CP_UMOUNT, "Umount" }, \
|
||||
{ CP_TRIMMED, "Trimmed" })
|
||||
|
||||
struct victim_sel_policy;
|
||||
struct f2fs_map_blocks;
|
||||
@@ -237,7 +250,7 @@ TRACE_EVENT(f2fs_sync_fs,
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), superblock is %s, wait = %d",
|
||||
show_dev(__entry),
|
||||
show_dev(__entry->dev),
|
||||
__entry->dirty ? "dirty" : "not dirty",
|
||||
__entry->wait)
|
||||
);
|
||||
@@ -307,6 +320,13 @@ DEFINE_EVENT(f2fs__inode_exit, f2fs_unlink_exit,
|
||||
TP_ARGS(inode, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__inode_exit, f2fs_drop_inode,
|
||||
|
||||
TP_PROTO(struct inode *inode, int ret),
|
||||
|
||||
TP_ARGS(inode, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__inode, f2fs_truncate,
|
||||
|
||||
TP_PROTO(struct inode *inode),
|
||||
@@ -516,14 +536,14 @@ TRACE_EVENT(f2fs_map_blocks,
|
||||
|
||||
TRACE_EVENT(f2fs_background_gc,
|
||||
|
||||
TP_PROTO(struct super_block *sb, long wait_ms,
|
||||
TP_PROTO(struct super_block *sb, unsigned int wait_ms,
|
||||
unsigned int prefree, unsigned int free),
|
||||
|
||||
TP_ARGS(sb, wait_ms, prefree, free),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(long, wait_ms)
|
||||
__field(unsigned int, wait_ms)
|
||||
__field(unsigned int, prefree)
|
||||
__field(unsigned int, free)
|
||||
),
|
||||
@@ -535,13 +555,120 @@ TRACE_EVENT(f2fs_background_gc,
|
||||
__entry->free = free;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u",
|
||||
show_dev(__entry),
|
||||
TP_printk("dev = (%d,%d), wait_ms = %u, prefree = %u, free = %u",
|
||||
show_dev(__entry->dev),
|
||||
__entry->wait_ms,
|
||||
__entry->prefree,
|
||||
__entry->free)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_gc_begin,
|
||||
|
||||
TP_PROTO(struct super_block *sb, bool sync, bool background,
|
||||
long long dirty_nodes, long long dirty_dents,
|
||||
long long dirty_imeta, unsigned int free_sec,
|
||||
unsigned int free_seg, int reserved_seg,
|
||||
unsigned int prefree_seg),
|
||||
|
||||
TP_ARGS(sb, sync, background, dirty_nodes, dirty_dents, dirty_imeta,
|
||||
free_sec, free_seg, reserved_seg, prefree_seg),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(bool, sync)
|
||||
__field(bool, background)
|
||||
__field(long long, dirty_nodes)
|
||||
__field(long long, dirty_dents)
|
||||
__field(long long, dirty_imeta)
|
||||
__field(unsigned int, free_sec)
|
||||
__field(unsigned int, free_seg)
|
||||
__field(int, reserved_seg)
|
||||
__field(unsigned int, prefree_seg)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->sync = sync;
|
||||
__entry->background = background;
|
||||
__entry->dirty_nodes = dirty_nodes;
|
||||
__entry->dirty_dents = dirty_dents;
|
||||
__entry->dirty_imeta = dirty_imeta;
|
||||
__entry->free_sec = free_sec;
|
||||
__entry->free_seg = free_seg;
|
||||
__entry->reserved_seg = reserved_seg;
|
||||
__entry->prefree_seg = prefree_seg;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), sync = %d, background = %d, nodes = %lld, "
|
||||
"dents = %lld, imeta = %lld, free_sec:%u, free_seg:%u, "
|
||||
"rsv_seg:%d, prefree_seg:%u",
|
||||
show_dev(__entry->dev),
|
||||
__entry->sync,
|
||||
__entry->background,
|
||||
__entry->dirty_nodes,
|
||||
__entry->dirty_dents,
|
||||
__entry->dirty_imeta,
|
||||
__entry->free_sec,
|
||||
__entry->free_seg,
|
||||
__entry->reserved_seg,
|
||||
__entry->prefree_seg)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_gc_end,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int ret, int seg_freed,
|
||||
int sec_freed, long long dirty_nodes,
|
||||
long long dirty_dents, long long dirty_imeta,
|
||||
unsigned int free_sec, unsigned int free_seg,
|
||||
int reserved_seg, unsigned int prefree_seg),
|
||||
|
||||
TP_ARGS(sb, ret, seg_freed, sec_freed, dirty_nodes, dirty_dents,
|
||||
dirty_imeta, free_sec, free_seg, reserved_seg, prefree_seg),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(int, ret)
|
||||
__field(int, seg_freed)
|
||||
__field(int, sec_freed)
|
||||
__field(long long, dirty_nodes)
|
||||
__field(long long, dirty_dents)
|
||||
__field(long long, dirty_imeta)
|
||||
__field(unsigned int, free_sec)
|
||||
__field(unsigned int, free_seg)
|
||||
__field(int, reserved_seg)
|
||||
__field(unsigned int, prefree_seg)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->ret = ret;
|
||||
__entry->seg_freed = seg_freed;
|
||||
__entry->sec_freed = sec_freed;
|
||||
__entry->dirty_nodes = dirty_nodes;
|
||||
__entry->dirty_dents = dirty_dents;
|
||||
__entry->dirty_imeta = dirty_imeta;
|
||||
__entry->free_sec = free_sec;
|
||||
__entry->free_seg = free_seg;
|
||||
__entry->reserved_seg = reserved_seg;
|
||||
__entry->prefree_seg = prefree_seg;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ret = %d, seg_freed = %d, sec_freed = %d, "
|
||||
"nodes = %lld, dents = %lld, imeta = %lld, free_sec:%u, "
|
||||
"free_seg:%u, rsv_seg:%d, prefree_seg:%u",
|
||||
show_dev(__entry->dev),
|
||||
__entry->ret,
|
||||
__entry->seg_freed,
|
||||
__entry->sec_freed,
|
||||
__entry->dirty_nodes,
|
||||
__entry->dirty_dents,
|
||||
__entry->dirty_imeta,
|
||||
__entry->free_sec,
|
||||
__entry->free_seg,
|
||||
__entry->reserved_seg,
|
||||
__entry->prefree_seg)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_get_victim,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, int gc_type,
|
||||
@@ -557,6 +684,7 @@ TRACE_EVENT(f2fs_get_victim,
|
||||
__field(int, alloc_mode)
|
||||
__field(int, gc_mode)
|
||||
__field(unsigned int, victim)
|
||||
__field(unsigned int, cost)
|
||||
__field(unsigned int, ofs_unit)
|
||||
__field(unsigned int, pre_victim)
|
||||
__field(unsigned int, prefree)
|
||||
@@ -570,20 +698,23 @@ TRACE_EVENT(f2fs_get_victim,
|
||||
__entry->alloc_mode = p->alloc_mode;
|
||||
__entry->gc_mode = p->gc_mode;
|
||||
__entry->victim = p->min_segno;
|
||||
__entry->cost = p->min_cost;
|
||||
__entry->ofs_unit = p->ofs_unit;
|
||||
__entry->pre_victim = pre_victim;
|
||||
__entry->prefree = prefree;
|
||||
__entry->free = free;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), type = %s, policy = (%s, %s, %s), victim = %u "
|
||||
"ofs_unit = %u, pre_victim_secno = %d, prefree = %u, free = %u",
|
||||
show_dev(__entry),
|
||||
TP_printk("dev = (%d,%d), type = %s, policy = (%s, %s, %s), "
|
||||
"victim = %u, cost = %u, ofs_unit = %u, "
|
||||
"pre_victim_secno = %d, prefree = %u, free = %u",
|
||||
show_dev(__entry->dev),
|
||||
show_data_type(__entry->type),
|
||||
show_gc_type(__entry->gc_type),
|
||||
show_alloc_mode(__entry->alloc_mode),
|
||||
show_victim_policy(__entry->gc_mode),
|
||||
__entry->victim,
|
||||
__entry->cost,
|
||||
__entry->ofs_unit,
|
||||
(int)__entry->pre_victim,
|
||||
__entry->prefree,
|
||||
@@ -715,7 +846,7 @@ TRACE_EVENT(f2fs_reserve_new_blocks,
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u, count = %llu",
|
||||
show_dev(__entry),
|
||||
show_dev(__entry->dev),
|
||||
(unsigned int)__entry->nid,
|
||||
__entry->ofs_in_node,
|
||||
(unsigned long long)__entry->count)
|
||||
@@ -735,6 +866,7 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
||||
__field(block_t, new_blkaddr)
|
||||
__field(int, op)
|
||||
__field(int, op_flags)
|
||||
__field(int, temp)
|
||||
__field(int, type)
|
||||
),
|
||||
|
||||
@@ -746,16 +878,18 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
||||
__entry->new_blkaddr = fio->new_blkaddr;
|
||||
__entry->op = fio->op;
|
||||
__entry->op_flags = fio->op_flags;
|
||||
__entry->temp = fio->temp;
|
||||
__entry->type = fio->type;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
||||
"oldaddr = 0x%llx, newaddr = 0x%llx, rw = %s%s, type = %s",
|
||||
"oldaddr = 0x%llx, newaddr = 0x%llx, rw = %s(%s), type = %s_%s",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->old_blkaddr,
|
||||
(unsigned long long)__entry->new_blkaddr,
|
||||
show_bio_type(__entry->op_flags),
|
||||
show_bio_type(__entry->op, __entry->op_flags),
|
||||
show_block_temp(__entry->temp),
|
||||
show_block_type(__entry->type))
|
||||
);
|
||||
|
||||
@@ -768,7 +902,7 @@ DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_bio,
|
||||
TP_CONDITION(page->mapping)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio,
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_write,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
@@ -777,15 +911,15 @@ DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio,
|
||||
TP_CONDITION(page->mapping)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
||||
DECLARE_EVENT_CLASS(f2fs__bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, int type, struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, fio, bio),
|
||||
TP_ARGS(sb, type, bio),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(dev_t, target)
|
||||
__field(int, op)
|
||||
__field(int, op_flags)
|
||||
__field(int, type)
|
||||
@@ -795,37 +929,55 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->op = fio->op;
|
||||
__entry->op_flags = fio->op_flags;
|
||||
__entry->type = fio->type;
|
||||
__entry->target = bio->bi_bdev->bd_dev;
|
||||
__entry->op = bio_op(bio);
|
||||
__entry->op_flags = bio->bi_opf;
|
||||
__entry->type = type;
|
||||
__entry->sector = bio->bi_iter.bi_sector;
|
||||
__entry->size = bio->bi_iter.bi_size;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), rw = %s%s, %s, sector = %lld, size = %u",
|
||||
show_dev(__entry),
|
||||
show_bio_type(__entry->op_flags),
|
||||
TP_printk("dev = (%d,%d)/(%d,%d), rw = %s(%s), %s, sector = %lld, size = %u",
|
||||
show_dev(__entry->target),
|
||||
show_dev(__entry->dev),
|
||||
show_bio_type(__entry->op, __entry->op_flags),
|
||||
show_block_type(__entry->type),
|
||||
(unsigned long long)__entry->sector,
|
||||
__entry->size)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_write_bio,
|
||||
DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_prepare_write_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, int type, struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, fio, bio),
|
||||
TP_ARGS(sb, type, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio,
|
||||
DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_prepare_read_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, int type, struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, fio, bio),
|
||||
TP_ARGS(sb, type, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_submit_read_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, type, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_submit_write_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, type, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
@@ -1084,16 +1236,16 @@ TRACE_EVENT(f2fs_write_checkpoint,
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), checkpoint for %s, state = %s",
|
||||
show_dev(__entry),
|
||||
show_dev(__entry->dev),
|
||||
show_cpreason(__entry->reason),
|
||||
__entry->msg)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_issue_discard,
|
||||
DECLARE_EVENT_CLASS(f2fs_discard,
|
||||
|
||||
TP_PROTO(struct super_block *sb, block_t blkstart, block_t blklen),
|
||||
TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen),
|
||||
|
||||
TP_ARGS(sb, blkstart, blklen),
|
||||
TP_ARGS(dev, blkstart, blklen),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
@@ -1102,40 +1254,78 @@ TRACE_EVENT(f2fs_issue_discard,
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->dev = dev->bd_dev;
|
||||
__entry->blkstart = blkstart;
|
||||
__entry->blklen = blklen;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), blkstart = 0x%llx, blklen = 0x%llx",
|
||||
show_dev(__entry),
|
||||
show_dev(__entry->dev),
|
||||
(unsigned long long)__entry->blkstart,
|
||||
(unsigned long long)__entry->blklen)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs_discard, f2fs_queue_discard,
|
||||
|
||||
TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen),
|
||||
|
||||
TP_ARGS(dev, blkstart, blklen)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs_discard, f2fs_issue_discard,
|
||||
|
||||
TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen),
|
||||
|
||||
TP_ARGS(dev, blkstart, blklen)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_issue_reset_zone,
|
||||
|
||||
TP_PROTO(struct block_device *dev, block_t blkstart),
|
||||
|
||||
TP_ARGS(dev, blkstart),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(block_t, blkstart)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = dev->bd_dev;
|
||||
__entry->blkstart = blkstart;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), reset zone at block = 0x%llx",
|
||||
show_dev(__entry->dev),
|
||||
(unsigned long long)__entry->blkstart)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_issue_flush,
|
||||
|
||||
TP_PROTO(struct super_block *sb, unsigned int nobarrier,
|
||||
unsigned int flush_merge),
|
||||
TP_PROTO(struct block_device *dev, unsigned int nobarrier,
|
||||
unsigned int flush_merge, int ret),
|
||||
|
||||
TP_ARGS(sb, nobarrier, flush_merge),
|
||||
TP_ARGS(dev, nobarrier, flush_merge, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(unsigned int, nobarrier)
|
||||
__field(unsigned int, flush_merge)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->dev = dev->bd_dev;
|
||||
__entry->nobarrier = nobarrier;
|
||||
__entry->flush_merge = flush_merge;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), %s %s",
|
||||
show_dev(__entry),
|
||||
TP_printk("dev = (%d,%d), %s %s, ret = %d",
|
||||
show_dev(__entry->dev),
|
||||
__entry->nobarrier ? "skip (nobarrier)" : "issue",
|
||||
__entry->flush_merge ? " with flush_merge" : "")
|
||||
__entry->flush_merge ? " with flush_merge" : "",
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_lookup_extent_tree_start,
|
||||
@@ -1248,7 +1438,7 @@ TRACE_EVENT(f2fs_shrink_extent_tree,
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u",
|
||||
show_dev(__entry),
|
||||
show_dev(__entry->dev),
|
||||
__entry->node_cnt,
|
||||
__entry->tree_cnt)
|
||||
);
|
||||
@@ -1295,7 +1485,7 @@ DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes,
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), %s, dirty count = %lld",
|
||||
show_dev(__entry),
|
||||
show_dev(__entry->dev),
|
||||
show_file_type(__entry->type),
|
||||
__entry->count)
|
||||
);
|
||||
|
||||
+30
-1
@@ -257,18 +257,47 @@ struct fsxattr {
|
||||
/* Policy provided via an ioctl on the topmost directory */
|
||||
#define FS_KEY_DESCRIPTOR_SIZE 8
|
||||
|
||||
#define FS_POLICY_FLAGS_PAD_4 0x00
|
||||
#define FS_POLICY_FLAGS_PAD_8 0x01
|
||||
#define FS_POLICY_FLAGS_PAD_16 0x02
|
||||
#define FS_POLICY_FLAGS_PAD_32 0x03
|
||||
#define FS_POLICY_FLAGS_PAD_MASK 0x03
|
||||
#define FS_POLICY_FLAGS_VALID 0x03
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define FS_ENCRYPTION_MODE_INVALID 0
|
||||
#define FS_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define FS_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define FS_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define FS_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
#define FS_ENCRYPTION_MODE_AES_128_CBC 5
|
||||
#define FS_ENCRYPTION_MODE_AES_128_CTS 6
|
||||
|
||||
struct fscrypt_policy {
|
||||
__u8 version;
|
||||
__u8 contents_encryption_mode;
|
||||
__u8 filenames_encryption_mode;
|
||||
__u8 flags;
|
||||
__u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
} __packed;
|
||||
};
|
||||
|
||||
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
|
||||
#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
|
||||
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
|
||||
|
||||
/* Parameters for passing an encryption key into the kernel keyring */
|
||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||
|
||||
/* Structure that userspace passes to the kernel keyring */
|
||||
#define FS_MAX_KEY_SIZE 64
|
||||
|
||||
struct fscrypt_key {
|
||||
__u32 mode;
|
||||
__u8 raw[FS_MAX_KEY_SIZE];
|
||||
__u32 size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
|
||||
*
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ static int digsig_verify_rsa(struct key *key,
|
||||
struct pubkey_hdr *pkh;
|
||||
|
||||
down_read(&key->sem);
|
||||
ukp = user_key_payload(key);
|
||||
ukp = user_key_payload_locked(key);
|
||||
|
||||
if (ukp->datalen < sizeof(*pkh))
|
||||
goto err1;
|
||||
|
||||
@@ -70,7 +70,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
|
||||
const char *options, char **_result, time64_t *_expiry)
|
||||
{
|
||||
struct key *rkey;
|
||||
const struct user_key_payload *upayload;
|
||||
struct user_key_payload *upayload;
|
||||
const struct cred *saved_cred;
|
||||
size_t typelen, desclen;
|
||||
char *desc, *cp;
|
||||
@@ -141,7 +141,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
|
||||
if (ret)
|
||||
goto put;
|
||||
|
||||
upayload = user_key_payload(rkey);
|
||||
upayload = user_key_payload_locked(rkey);
|
||||
len = upayload->datalen;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
|
||||
if (status == 0) {
|
||||
const struct user_key_payload *payload;
|
||||
|
||||
payload = user_key_payload(key);
|
||||
payload = user_key_payload_locked(key);
|
||||
|
||||
if (maxlen == 0) {
|
||||
*mpi = NULL;
|
||||
|
||||
@@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k
|
||||
goto error;
|
||||
|
||||
down_read(&ukey->sem);
|
||||
upayload = user_key_payload(ukey);
|
||||
upayload = user_key_payload_locked(ukey);
|
||||
*master_key = upayload->data;
|
||||
*master_keylen = upayload->datalen;
|
||||
error:
|
||||
@@ -927,7 +927,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
|
||||
size_t asciiblob_len;
|
||||
int ret;
|
||||
|
||||
epayload = rcu_dereference_key(key);
|
||||
epayload = dereference_key_locked(key);
|
||||
|
||||
/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
|
||||
asciiblob_len = epayload->datablob_len + ivsize + 1
|
||||
|
||||
@@ -1140,12 +1140,12 @@ out:
|
||||
static long trusted_read(const struct key *key, char __user *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
struct trusted_key_payload *p;
|
||||
const struct trusted_key_payload *p;
|
||||
char *ascii_buf;
|
||||
char *bufp;
|
||||
int i;
|
||||
|
||||
p = rcu_dereference_key(key);
|
||||
p = dereference_key_locked(key);
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
if (!buffer || buflen <= 0)
|
||||
|
||||
@@ -107,7 +107,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
/* attach the new data, displacing the old */
|
||||
key->expiry = prep->expiry;
|
||||
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
||||
zap = rcu_dereference_key(key);
|
||||
zap = dereference_key_locked(key);
|
||||
rcu_assign_keypointer(key, prep->payload.data[0]);
|
||||
prep->payload.data[0] = NULL;
|
||||
|
||||
@@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(user_update);
|
||||
*/
|
||||
void user_revoke(struct key *key)
|
||||
{
|
||||
struct user_key_payload *upayload = key->payload.data[0];
|
||||
struct user_key_payload *upayload = user_key_payload_locked(key);
|
||||
|
||||
/* clear the quota */
|
||||
key_payload_reserve(key, 0);
|
||||
@@ -169,7 +169,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
const struct user_key_payload *upayload;
|
||||
long ret;
|
||||
|
||||
upayload = user_key_payload(key);
|
||||
upayload = user_key_payload_locked(key);
|
||||
ret = upayload->datalen;
|
||||
|
||||
/* we can return the data as is */
|
||||
|
||||
Reference in New Issue
Block a user