Merge tag 'for-f2fs-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs

Pull f2fs updates from Jaegeuk Kim:
 "New Features:
   - uplift filesystem encryption into fs/crypto/
   - give sysfs entries to control memroy consumption

  Enhancements:
   - aio performance by preallocating blocks in ->write_iter
   - use writepages lock for only WB_SYNC_ALL
   - avoid redundant inline_data conversion
   - enhance forground GC
   - use wait_for_stable_page as possible
   - speed up SEEK_DATA and fiiemap

  Bug Fixes:
   - corner case in terms of -ENOSPC for inline_data
   - hung task caused by long latency in shrinker
   - corruption between atomic write and f2fs_trace_pid
   - avoid garbage lengths in dentries
   - revoke atomicly written pages if an error occurs

  In addition, there are various minor bug fixes and clean-ups"

* tag 'for-f2fs-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (81 commits)
  f2fs: submit node page write bios when really required
  f2fs: add missing argument to f2fs_setxattr stub
  f2fs: fix to avoid unneeded unlock_new_inode
  f2fs: clean up opened code with f2fs_update_dentry
  f2fs: declare static functions
  f2fs: use cryptoapi crc32 functions
  f2fs: modify the readahead method in ra_node_page()
  f2fs crypto: sync ext4_lookup and ext4_file_open
  fs crypto: move per-file encryption from f2fs tree to fs/crypto
  f2fs: mutex can't be used by down_write_nest_lock()
  f2fs: recovery missing dot dentries in root directory
  f2fs: fix to avoid deadlock when merging inline data
  f2fs: introduce f2fs_flush_merged_bios for cleanup
  f2fs: introduce f2fs_update_data_blkaddr for cleanup
  f2fs crypto: fix incorrect positioning for GCing encrypted data page
  f2fs: fix incorrect upper bound when iterating inode mapping tree
  f2fs: avoid hungtask problem caused by losing wake_up
  f2fs: trace old block address for CoWed page
  f2fs: try to flush inode after merging inline data
  f2fs: show more info about superblock recovery
  ...
This commit is contained in:
Linus Torvalds 2016-03-21 11:03:02 -07:00
commit d407574e79
40 changed files with 3148 additions and 2385 deletions

View File

@ -98,3 +98,17 @@ Date: October 2015
Contact: "Chao Yu" <chao2.yu@samsung.com> Contact: "Chao Yu" <chao2.yu@samsung.com>
Description: Description:
Controls the count of nid pages to be readaheaded. Controls the count of nid pages to be readaheaded.
What: /sys/fs/f2fs/<disk>/dirty_nats_ratio
Date: January 2016
Contact: "Chao Yu" <chao2.yu@samsung.com>
Description:
Controls dirty nat entries ratio threshold, if current
ratio exceeds configured threshold, checkpoint will
be triggered for flushing dirty nat entries.
What: /sys/fs/f2fs/<disk>/lifetime_write_kbytes
Date: January 2016
Contact: "Shuoran Liu" <liushuoran@huawei.com>
Description:
Shows total written kbytes issued to disk.

View File

@ -84,6 +84,8 @@ config MANDATORY_FILE_LOCKING
To the best of my knowledge this is dead code that no one cares about. To the best of my knowledge this is dead code that no one cares about.
source "fs/crypto/Kconfig"
source "fs/notify/Kconfig" source "fs/notify/Kconfig"
source "fs/quota/Kconfig" source "fs/quota/Kconfig"

View File

@ -30,6 +30,7 @@ obj-$(CONFIG_EVENTFD) += eventfd.o
obj-$(CONFIG_USERFAULTFD) += userfaultfd.o obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
obj-$(CONFIG_AIO) += aio.o obj-$(CONFIG_AIO) += aio.o
obj-$(CONFIG_FS_DAX) += dax.o obj-$(CONFIG_FS_DAX) += dax.o
obj-$(CONFIG_FS_ENCRYPTION) += crypto/
obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_FILE_LOCKING) += locks.o
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o

18
fs/crypto/Kconfig Normal file
View File

@ -0,0 +1,18 @@
config FS_ENCRYPTION
tristate "FS Encryption (Per-file encryption)"
depends on BLOCK
select CRYPTO
select CRYPTO_AES
select CRYPTO_CBC
select CRYPTO_ECB
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
efficient since it avoids caching the encrypted and
decrypted pages in the page cache.

3
fs/crypto/Makefile Normal file
View File

@ -0,0 +1,3 @@
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
fscrypto-y := crypto.o fname.o policy.o keyinfo.o

555
fs/crypto/crypto.c Normal file
View File

@ -0,0 +1,555 @@
/*
* 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/mempool.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/ratelimit.h>
#include <linux/bio.h>
#include <linux/dcache.h>
#include <linux/fscrypto.h>
#include <linux/ecryptfs.h>
static unsigned int num_prealloc_crypto_pages = 32;
static unsigned int num_prealloc_crypto_ctxs = 128;
module_param(num_prealloc_crypto_pages, uint, 0444);
MODULE_PARM_DESC(num_prealloc_crypto_pages,
"Number of crypto pages to preallocate");
module_param(num_prealloc_crypto_ctxs, uint, 0444);
MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
"Number of crypto contexts to preallocate");
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;
static DEFINE_MUTEX(fscrypt_init_mutex);
static struct kmem_cache *fscrypt_ctx_cachep;
struct kmem_cache *fscrypt_info_cachep;
/**
* fscrypt_release_ctx() - Releases an encryption context
* @ctx: The encryption context to release.
*
* If the encryption context was allocated from the pre-allocated pool, returns
* it to that pool. Else, frees it.
*
* If there's a bounce page in the context, this frees that.
*/
void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
{
unsigned long flags;
if (ctx->flags & FS_WRITE_PATH_FL && ctx->w.bounce_page) {
mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool);
ctx->w.bounce_page = NULL;
}
ctx->w.control_page = NULL;
if (ctx->flags & FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
kmem_cache_free(fscrypt_ctx_cachep, ctx);
} else {
spin_lock_irqsave(&fscrypt_ctx_lock, flags);
list_add(&ctx->free_list, &fscrypt_free_ctxs);
spin_unlock_irqrestore(&fscrypt_ctx_lock, flags);
}
}
EXPORT_SYMBOL(fscrypt_release_ctx);
/**
* fscrypt_get_ctx() - Gets an encryption context
* @inode: The inode for which we are doing the crypto
*
* Allocates and initializes an encryption context.
*
* Return: An allocated and initialized encryption context on success; error
* value or NULL otherwise.
*/
struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode)
{
struct fscrypt_ctx *ctx = NULL;
struct fscrypt_info *ci = inode->i_crypt_info;
unsigned long flags;
if (ci == NULL)
return ERR_PTR(-ENOKEY);
/*
* We first try getting the ctx from a free list because in
* the common case the ctx will have an allocated and
* initialized crypto tfm, so it's probably a worthwhile
* optimization. For the bounce page, we first try getting it
* from the kernel allocator because that's just about as fast
* as getting it from a list and because a cache of free pages
* should generally be a "last resort" option for a filesystem
* to be able to do its job.
*/
spin_lock_irqsave(&fscrypt_ctx_lock, flags);
ctx = list_first_entry_or_null(&fscrypt_free_ctxs,
struct fscrypt_ctx, free_list);
if (ctx)
list_del(&ctx->free_list);
spin_unlock_irqrestore(&fscrypt_ctx_lock, flags);
if (!ctx) {
ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->flags |= FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
} else {
ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
}
ctx->flags &= ~FS_WRITE_PATH_FL;
return ctx;
}
EXPORT_SYMBOL(fscrypt_get_ctx);
/**
* fscrypt_complete() - The completion callback for page encryption
* @req: The asynchronous encryption request context
* @res: The result of the encryption operation
*/
static void fscrypt_complete(struct crypto_async_request *req, int res)
{
struct fscrypt_completion_result *ecr = req->data;
if (res == -EINPROGRESS)
return;
ecr->res = 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)
{
u8 xts_tweak[FS_XTS_TWEAK_SIZE];
struct skcipher_request *req = NULL;
DECLARE_FS_COMPLETION_RESULT(ecr);
struct scatterlist dst, src;
struct fscrypt_info *ci = inode->i_crypt_info;
struct crypto_skcipher *tfm = ci->ci_ctfm;
int res = 0;
req = skcipher_request_alloc(tfm, GFP_NOFS);
if (!req) {
printk_ratelimited(KERN_ERR
"%s: crypto_request_alloc() failed\n",
__func__);
return -ENOMEM;
}
skcipher_request_set_callback(
req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
fscrypt_complete, &ecr);
BUILD_BUG_ON(FS_XTS_TWEAK_SIZE < sizeof(index));
memcpy(xts_tweak, &inode->i_ino, sizeof(index));
memset(&xts_tweak[sizeof(index)], 0,
FS_XTS_TWEAK_SIZE - sizeof(index));
sg_init_table(&dst, 1);
sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0);
sg_init_table(&src, 1);
sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0);
skcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
xts_tweak);
if (rw == FS_DECRYPT)
res = crypto_skcipher_decrypt(req);
else
res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) {
BUG_ON(req->base.data != &ecr);
wait_for_completion(&ecr.completion);
res = ecr.res;
}
skcipher_request_free(req);
if (res) {
printk_ratelimited(KERN_ERR
"%s: crypto_skcipher_encrypt() returned %d\n",
__func__, res);
return res;
}
return 0;
}
static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx)
{
ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool,
GFP_NOWAIT);
if (ctx->w.bounce_page == NULL)
return ERR_PTR(-ENOMEM);
ctx->flags |= FS_WRITE_PATH_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.
*
* Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
* encryption context.
*
* Called on the page write path. 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
* error value or NULL.
*/
struct page *fscrypt_encrypt_page(struct inode *inode,
struct page *plaintext_page)
{
struct fscrypt_ctx *ctx;
struct page *ciphertext_page = NULL;
int err;
BUG_ON(!PageLocked(plaintext_page));
ctx = fscrypt_get_ctx(inode);
if (IS_ERR(ctx))
return (struct page *)ctx;
/* The encryption operation will require a bounce page. */
ciphertext_page = alloc_bounce_page(ctx);
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);
if (err) {
ciphertext_page = ERR_PTR(err);
goto errout;
}
SetPagePrivate(ciphertext_page);
set_page_private(ciphertext_page, (unsigned long)ctx);
lock_page(ciphertext_page);
return ciphertext_page;
errout:
fscrypt_release_ctx(ctx);
return ciphertext_page;
}
EXPORT_SYMBOL(fscrypt_encrypt_page);
/**
* f2crypt_decrypt_page() - Decrypts a page in-place
* @page: The page to decrypt. Must be locked.
*
* Decrypts page in-place using the ctx encryption context.
*
* Called from the read completion callback.
*
* Return: Zero on success, non-zero otherwise.
*/
int fscrypt_decrypt_page(struct page *page)
{
BUG_ON(!PageLocked(page));
return do_page_crypto(page->mapping->host,
FS_DECRYPT, page->index, page, page);
}
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_CACHE_SIZE);
ctx = fscrypt_get_ctx(inode);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ciphertext_page = alloc_bounce_page(ctx);
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);
if (err)
goto errout;
bio = bio_alloc(GFP_KERNEL, 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);
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(WRITE, 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
* removed.
*/
static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
{
struct inode *dir = d_inode(dentry->d_parent);
struct fscrypt_info *ci = dir->i_crypt_info;
int dir_has_key, cached_with_key;
if (!dir->i_sb->s_cop->is_encrypted(dir))
return 0;
if (ci && ci->ci_keyring_key &&
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED) |
(1 << KEY_FLAG_DEAD))))
ci = NULL;
/* this should eventually be an flag in d_flags */
spin_lock(&dentry->d_lock);
cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY;
spin_unlock(&dentry->d_lock);
dir_has_key = (ci != NULL);
/*
* If the dentry was cached without the key, and it is a
* negative dentry, it might be a valid name. We can't check
* if the key has since been made available due to locking
* reasons, so we fail the validation so ext4_lookup() can do
* this check.
*
* We also fail the validation if the dentry was created with
* the key present, but we no longer have the key, or vice versa.
*/
if ((!cached_with_key && d_is_negative(dentry)) ||
(!cached_with_key && dir_has_key) ||
(cached_with_key && !dir_has_key))
return 0;
return 1;
}
const struct dentry_operations fscrypt_d_ops = {
.d_revalidate = fscrypt_d_revalidate,
};
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;
ctx = (struct fscrypt_ctx *)page_private(page);
set_page_private(page, (unsigned long)NULL);
ClearPagePrivate(page);
unlock_page(page);
fscrypt_release_ctx(ctx);
}
EXPORT_SYMBOL(fscrypt_restore_control_page);
static void fscrypt_destroy(void)
{
struct fscrypt_ctx *pos, *n;
list_for_each_entry_safe(pos, n, &fscrypt_free_ctxs, free_list)
kmem_cache_free(fscrypt_ctx_cachep, pos);
INIT_LIST_HEAD(&fscrypt_free_ctxs);
mempool_destroy(fscrypt_bounce_page_pool);
fscrypt_bounce_page_pool = NULL;
}
/**
* fscrypt_initialize() - allocate major buffers for fs encryption.
*
* 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 i, res = -ENOMEM;
if (fscrypt_bounce_page_pool)
return 0;
mutex_lock(&fscrypt_init_mutex);
if (fscrypt_bounce_page_pool)
goto already_initialized;
for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
struct fscrypt_ctx *ctx;
ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS);
if (!ctx)
goto fail;
list_add(&ctx->free_list, &fscrypt_free_ctxs);
}
fscrypt_bounce_page_pool =
mempool_create_page_pool(num_prealloc_crypto_pages, 0);
if (!fscrypt_bounce_page_pool)
goto fail;
already_initialized:
mutex_unlock(&fscrypt_init_mutex);
return 0;
fail:
fscrypt_destroy();
mutex_unlock(&fscrypt_init_mutex);
return res;
}
EXPORT_SYMBOL(fscrypt_initialize);
/**
* fscrypt_init() - Set up for fs encryption.
*/
static int __init fscrypt_init(void)
{
fscrypt_read_workqueue = alloc_workqueue("fscrypt_read_queue",
WQ_HIGHPRI, 0);
if (!fscrypt_read_workqueue)
goto fail;
fscrypt_ctx_cachep = KMEM_CACHE(fscrypt_ctx, SLAB_RECLAIM_ACCOUNT);
if (!fscrypt_ctx_cachep)
goto fail_free_queue;
fscrypt_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT);
if (!fscrypt_info_cachep)
goto fail_free_ctx;
return 0;
fail_free_ctx:
kmem_cache_destroy(fscrypt_ctx_cachep);
fail_free_queue:
destroy_workqueue(fscrypt_read_workqueue);
fail:
return -ENOMEM;
}
module_init(fscrypt_init)
/**
* fscrypt_exit() - Shutdown the fs encryption system
*/
static void __exit fscrypt_exit(void)
{
fscrypt_destroy();
if (fscrypt_read_workqueue)
destroy_workqueue(fscrypt_read_workqueue);
kmem_cache_destroy(fscrypt_ctx_cachep);
kmem_cache_destroy(fscrypt_info_cachep);
}
module_exit(fscrypt_exit);
MODULE_LICENSE("GPL");

View File

@ -1,44 +1,32 @@
/* /*
* linux/fs/f2fs/crypto_fname.c * This contains functions for filename crypto management
*
* Copied from linux/fs/ext4/crypto.c
* *
* Copyright (C) 2015, Google, Inc. * Copyright (C) 2015, Google, Inc.
* Copyright (C) 2015, Motorola Mobility * Copyright (C) 2015, Motorola Mobility
* *
* This contains functions for filename crypto management in f2fs
*
* Written by Uday Savagaonkar, 2014. * Written by Uday Savagaonkar, 2014.
* * Modified by Jaegeuk Kim, 2015.
* Adjust f2fs dentry structure
* Jaegeuk Kim, 2015.
* *
* This has not yet undergone a rigorous security audit. * This has not yet undergone a rigorous security audit.
*/ */
#include <crypto/skcipher.h>
#include <keys/encrypted-type.h> #include <keys/encrypted-type.h>
#include <keys/user-type.h> #include <keys/user-type.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/key.h>
#include <linux/list.h>
#include <linux/mempool.h>
#include <linux/random.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/spinlock_types.h>
#include <linux/f2fs_fs.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/fscrypto.h>
#include "f2fs.h" static u32 size_round_up(size_t size, size_t blksize)
#include "f2fs_crypto.h" {
#include "xattr.h" return ((size + blksize - 1) / blksize) * blksize;
}
/** /**
* f2fs_dir_crypt_complete() - * dir_crypt_complete() -
*/ */
static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res) static void dir_crypt_complete(struct crypto_async_request *req, int res)
{ {
struct f2fs_completion_result *ecr = req->data; struct fscrypt_completion_result *ecr = req->data;
if (res == -EINPROGRESS) if (res == -EINPROGRESS)
return; return;
@ -46,45 +34,35 @@ static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res)
complete(&ecr->completion); complete(&ecr->completion);
} }
bool f2fs_valid_filenames_enc_mode(uint32_t mode)
{
return (mode == F2FS_ENCRYPTION_MODE_AES_256_CTS);
}
static unsigned max_name_len(struct inode *inode)
{
return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize :
F2FS_NAME_LEN;
}
/** /**
* f2fs_fname_encrypt() - * fname_encrypt() -
* *
* This function encrypts the input filename, and returns the length of the * This function encrypts the input filename, and returns the length of the
* ciphertext. Errors are returned as negative numbers. We trust the caller to * ciphertext. Errors are returned as negative numbers. We trust the caller to
* allocate sufficient memory to oname string. * allocate sufficient memory to oname string.
*/ */
static int f2fs_fname_encrypt(struct inode *inode, static int fname_encrypt(struct inode *inode,
const struct qstr *iname, struct f2fs_str *oname) const struct qstr *iname, struct fscrypt_str *oname)
{ {
u32 ciphertext_len; u32 ciphertext_len;
struct skcipher_request *req = NULL; struct skcipher_request *req = NULL;
DECLARE_F2FS_COMPLETION_RESULT(ecr); DECLARE_FS_COMPLETION_RESULT(ecr);
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; struct fscrypt_info *ci = inode->i_crypt_info;
struct crypto_skcipher *tfm = ci->ci_ctfm; struct crypto_skcipher *tfm = ci->ci_ctfm;
int res = 0; int res = 0;
char iv[F2FS_CRYPTO_BLOCK_SIZE]; char iv[FS_CRYPTO_BLOCK_SIZE];
struct scatterlist src_sg, dst_sg; struct scatterlist src_sg, dst_sg;
int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
char *workbuf, buf[32], *alloc_buf = NULL; char *workbuf, buf[32], *alloc_buf = NULL;
unsigned lim = max_name_len(inode); unsigned lim;
lim = inode->i_sb->s_cop->max_namelen(inode);
if (iname->len <= 0 || iname->len > lim) if (iname->len <= 0 || iname->len > lim)
return -EIO; return -EIO;
ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ? ciphertext_len = (iname->len < FS_CRYPTO_BLOCK_SIZE) ?
F2FS_CRYPTO_BLOCK_SIZE : iname->len; FS_CRYPTO_BLOCK_SIZE : iname->len;
ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding); ciphertext_len = size_round_up(ciphertext_len, padding);
ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len; ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
if (ciphertext_len <= sizeof(buf)) { if (ciphertext_len <= sizeof(buf)) {
@ -106,7 +84,7 @@ static int f2fs_fname_encrypt(struct inode *inode,
} }
skcipher_request_set_callback(req, skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
f2fs_dir_crypt_complete, &ecr); dir_crypt_complete, &ecr);
/* Copy the input */ /* Copy the input */
memcpy(workbuf, iname->name, iname->len); memcpy(workbuf, iname->name, iname->len);
@ -114,7 +92,7 @@ static int f2fs_fname_encrypt(struct inode *inode,
memset(workbuf + iname->len, 0, ciphertext_len - iname->len); memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
/* Initialize IV */ /* Initialize IV */
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
/* Create encryption request */ /* Create encryption request */
sg_init_one(&src_sg, workbuf, ciphertext_len); sg_init_one(&src_sg, workbuf, ciphertext_len);
@ -122,39 +100,40 @@ static int f2fs_fname_encrypt(struct inode *inode,
skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
res = crypto_skcipher_encrypt(req); res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) { if (res == -EINPROGRESS || res == -EBUSY) {
BUG_ON(req->base.data != &ecr);
wait_for_completion(&ecr.completion); wait_for_completion(&ecr.completion);
res = ecr.res; res = ecr.res;
} }
kfree(alloc_buf); kfree(alloc_buf);
skcipher_request_free(req); skcipher_request_free(req);
if (res < 0) { if (res < 0)
printk_ratelimited(KERN_ERR printk_ratelimited(KERN_ERR
"%s: Error (error code %d)\n", __func__, res); "%s: Error (error code %d)\n", __func__, res);
}
oname->len = ciphertext_len; oname->len = ciphertext_len;
return res; return res;
} }
/* /*
* f2fs_fname_decrypt() * fname_decrypt()
* This function decrypts the input filename, and returns * This function decrypts the input filename, and returns
* the length of the plaintext. * the length of the plaintext.
* Errors are returned as negative numbers. * Errors are returned as negative numbers.
* We trust the caller to allocate sufficient memory to oname string. * We trust the caller to allocate sufficient memory to oname string.
*/ */
static int f2fs_fname_decrypt(struct inode *inode, static int fname_decrypt(struct inode *inode,
const struct f2fs_str *iname, struct f2fs_str *oname) const struct fscrypt_str *iname,
struct fscrypt_str *oname)
{ {
struct skcipher_request *req = NULL; struct skcipher_request *req = NULL;
DECLARE_F2FS_COMPLETION_RESULT(ecr); DECLARE_FS_COMPLETION_RESULT(ecr);
struct scatterlist src_sg, dst_sg; struct scatterlist src_sg, dst_sg;
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; struct fscrypt_info *ci = inode->i_crypt_info;
struct crypto_skcipher *tfm = ci->ci_ctfm; struct crypto_skcipher *tfm = ci->ci_ctfm;
int res = 0; int res = 0;
char iv[F2FS_CRYPTO_BLOCK_SIZE]; char iv[FS_CRYPTO_BLOCK_SIZE];
unsigned lim = max_name_len(inode); unsigned lim;
lim = inode->i_sb->s_cop->max_namelen(inode);
if (iname->len <= 0 || iname->len > lim) if (iname->len <= 0 || iname->len > lim)
return -EIO; return -EIO;
@ -167,10 +146,10 @@ static int f2fs_fname_decrypt(struct inode *inode,
} }
skcipher_request_set_callback(req, skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
f2fs_dir_crypt_complete, &ecr); dir_crypt_complete, &ecr);
/* Initialize IV */ /* Initialize IV */
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
/* Create decryption request */ /* Create decryption request */
sg_init_one(&src_sg, iname->name, iname->len); sg_init_one(&src_sg, iname->name, iname->len);
@ -178,15 +157,13 @@ static int f2fs_fname_decrypt(struct inode *inode,
skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
res = crypto_skcipher_decrypt(req); res = crypto_skcipher_decrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) { if (res == -EINPROGRESS || res == -EBUSY) {
BUG_ON(req->base.data != &ecr);
wait_for_completion(&ecr.completion); wait_for_completion(&ecr.completion);
res = ecr.res; res = ecr.res;
} }
skcipher_request_free(req); skcipher_request_free(req);
if (res < 0) { if (res < 0) {
printk_ratelimited(KERN_ERR printk_ratelimited(KERN_ERR
"%s: Error in f2fs_fname_decrypt (error code %d)\n", "%s: Error (error code %d)\n", __func__, res);
__func__, res);
return res; return res;
} }
@ -198,7 +175,7 @@ static const char *lookup_table =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
/** /**
* f2fs_fname_encode_digest() - * digest_encode() -
* *
* Encodes the input digest using characters from the set [a-zA-Z0-9_+]. * Encodes the input digest using characters from the set [a-zA-Z0-9_+].
* The encoded string is roughly 4/3 times the size of the input string. * The encoded string is roughly 4/3 times the size of the input string.
@ -247,148 +224,152 @@ static int digest_decode(const char *src, int len, char *dst)
return cp - dst; return cp - dst;
} }
/** u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
* f2fs_fname_crypto_round_up() -
*
* Return: The next multiple of block size
*/
u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize)
{ {
return ((size + blksize - 1) / blksize) * blksize; int padding = 32;
struct fscrypt_info *ci = inode->i_crypt_info;
if (ci)
padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
if (ilen < FS_CRYPTO_BLOCK_SIZE)
ilen = FS_CRYPTO_BLOCK_SIZE;
return size_round_up(ilen, padding);
} }
EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
/** /**
* f2fs_fname_crypto_alloc_obuff() - * fscrypt_fname_crypto_alloc_obuff() -
* *
* Allocates an output buffer that is sufficient for the crypto operation * Allocates an output buffer that is sufficient for the crypto operation
* specified by the context and the direction. * specified by the context and the direction.
*/ */
int f2fs_fname_crypto_alloc_buffer(struct inode *inode, int fscrypt_fname_alloc_buffer(struct inode *inode,
u32 ilen, struct f2fs_str *crypto_str) u32 ilen, struct fscrypt_str *crypto_str)
{ {
unsigned int olen; unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
int padding = 16;
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
if (ci)
padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK);
if (padding < F2FS_CRYPTO_BLOCK_SIZE)
padding = F2FS_CRYPTO_BLOCK_SIZE;
olen = f2fs_fname_crypto_round_up(ilen, padding);
crypto_str->len = olen; crypto_str->len = olen;
if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2) if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2; olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
/* Allocated buffer can hold one more character to null-terminate the /*
* string */ * Allocated buffer can hold one more character to null-terminate the
* string
*/
crypto_str->name = kmalloc(olen + 1, GFP_NOFS); crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
if (!(crypto_str->name)) if (!(crypto_str->name))
return -ENOMEM; return -ENOMEM;
return 0; return 0;
} }
EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
/** /**
* f2fs_fname_crypto_free_buffer() - * fscrypt_fname_crypto_free_buffer() -
* *
* Frees the buffer allocated for crypto operation. * Frees the buffer allocated for crypto operation.
*/ */
void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str) void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
{ {
if (!crypto_str) if (!crypto_str)
return; return;
kfree(crypto_str->name); kfree(crypto_str->name);
crypto_str->name = NULL; crypto_str->name = NULL;
} }
EXPORT_SYMBOL(fscrypt_fname_free_buffer);
/** /**
* f2fs_fname_disk_to_usr() - converts a filename from disk space to user space * fscrypt_fname_disk_to_usr() - converts a filename from disk space to user
* space
*/ */
int f2fs_fname_disk_to_usr(struct inode *inode, int fscrypt_fname_disk_to_usr(struct inode *inode,
f2fs_hash_t *hash, u32 hash, u32 minor_hash,
const struct f2fs_str *iname, const struct fscrypt_str *iname,
struct f2fs_str *oname) struct fscrypt_str *oname)
{ {
const struct qstr qname = FSTR_TO_QSTR(iname); const struct qstr qname = FSTR_TO_QSTR(iname);
char buf[24]; char buf[24];
int ret; int ret;
if (is_dot_dotdot(&qname)) { if (fscrypt_is_dot_dotdot(&qname)) {
oname->name[0] = '.'; oname->name[0] = '.';
oname->name[iname->len - 1] = '.'; oname->name[iname->len - 1] = '.';
oname->len = iname->len; oname->len = iname->len;
return oname->len; return oname->len;
} }
if (F2FS_I(inode)->i_crypt_info) if (iname->len < FS_CRYPTO_BLOCK_SIZE)
return f2fs_fname_decrypt(inode, iname, oname); return -EUCLEAN;
if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) { if (inode->i_crypt_info)
return fname_decrypt(inode, iname, oname);
if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) {
ret = digest_encode(iname->name, iname->len, oname->name); ret = digest_encode(iname->name, iname->len, oname->name);
oname->len = ret; oname->len = ret;
return ret; return ret;
} }
if (hash) { if (hash) {
memcpy(buf, hash, 4); memcpy(buf, &hash, 4);
memset(buf + 4, 0, 4); memcpy(buf + 4, &minor_hash, 4);
} else } else {
memset(buf, 0, 8); memset(buf, 0, 8);
}
memcpy(buf + 8, iname->name + iname->len - 16, 16); memcpy(buf + 8, iname->name + iname->len - 16, 16);
oname->name[0] = '_'; oname->name[0] = '_';
ret = digest_encode(buf, 24, oname->name + 1); ret = digest_encode(buf, 24, oname->name + 1);
oname->len = ret + 1; oname->len = ret + 1;
return ret + 1; return ret + 1;
} }
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
/** /**
* f2fs_fname_usr_to_disk() - converts a filename from user space to disk space * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk
* space
*/ */
int f2fs_fname_usr_to_disk(struct inode *inode, int fscrypt_fname_usr_to_disk(struct inode *inode,
const struct qstr *iname, const struct qstr *iname,
struct f2fs_str *oname) struct fscrypt_str *oname)
{ {
int res; if (fscrypt_is_dot_dotdot(iname)) {
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
if (is_dot_dotdot(iname)) {
oname->name[0] = '.'; oname->name[0] = '.';
oname->name[iname->len - 1] = '.'; oname->name[iname->len - 1] = '.';
oname->len = iname->len; oname->len = iname->len;
return oname->len; return oname->len;
} }
if (inode->i_crypt_info)
if (ci) { return fname_encrypt(inode, iname, oname);
res = f2fs_fname_encrypt(inode, iname, oname); /*
return res; * Without a proper key, a user is not allowed to modify the filenames
}
/* Without a proper key, a user is not allowed to modify the filenames
* in a directory. Consequently, a user space name cannot be mapped to * in a directory. Consequently, a user space name cannot be mapped to
* a disk-space name */ * a disk-space name
*/
return -EACCES; return -EACCES;
} }
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname, int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
int lookup, struct f2fs_filename *fname) int lookup, struct fscrypt_name *fname)
{ {
struct f2fs_crypt_info *ci;
int ret = 0, bigname = 0; int ret = 0, bigname = 0;
memset(fname, 0, sizeof(struct f2fs_filename)); memset(fname, 0, sizeof(struct fscrypt_name));
fname->usr_fname = iname; fname->usr_fname = iname;
if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(iname)) { if (!dir->i_sb->s_cop->is_encrypted(dir) ||
fscrypt_is_dot_dotdot(iname)) {
fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.name = (unsigned char *)iname->name;
fname->disk_name.len = iname->len; fname->disk_name.len = iname->len;
return 0; return 0;
} }
ret = f2fs_get_encryption_info(dir); ret = get_crypt_info(dir);
if (ret) if (ret && ret != -EOPNOTSUPP)
return ret; return ret;
ci = F2FS_I(dir)->i_crypt_info;
if (ci) { if (dir->i_crypt_info) {
ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len, ret = fscrypt_fname_alloc_buffer(dir, iname->len,
&fname->crypto_buf); &fname->crypto_buf);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf); ret = fname_encrypt(dir, iname, &fname->crypto_buf);
if (ret < 0) if (ret < 0)
goto errout; goto errout;
fname->disk_name.name = fname->crypto_buf.name; fname->disk_name.name = fname->crypto_buf.name;
@ -398,18 +379,19 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
if (!lookup) if (!lookup)
return -EACCES; return -EACCES;
/* We don't have the key and we are doing a lookup; decode the /*
* We don't have the key and we are doing a lookup; decode the
* user-supplied name * user-supplied name
*/ */
if (iname->name[0] == '_') if (iname->name[0] == '_')
bigname = 1; bigname = 1;
if ((bigname && (iname->len != 33)) || if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
(!bigname && (iname->len > 43)))
return -ENOENT; return -ENOENT;
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
if (fname->crypto_buf.name == NULL) if (fname->crypto_buf.name == NULL)
return -ENOMEM; return -ENOMEM;
ret = digest_decode(iname->name + bigname, iname->len - bigname, ret = digest_decode(iname->name + bigname, iname->len - bigname,
fname->crypto_buf.name); fname->crypto_buf.name);
if (ret < 0) { if (ret < 0) {
@ -419,20 +401,24 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
fname->crypto_buf.len = ret; fname->crypto_buf.len = ret;
if (bigname) { if (bigname) {
memcpy(&fname->hash, fname->crypto_buf.name, 4); memcpy(&fname->hash, fname->crypto_buf.name, 4);
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
} else { } else {
fname->disk_name.name = fname->crypto_buf.name; fname->disk_name.name = fname->crypto_buf.name;
fname->disk_name.len = fname->crypto_buf.len; fname->disk_name.len = fname->crypto_buf.len;
} }
return 0; return 0;
errout: errout:
f2fs_fname_crypto_free_buffer(&fname->crypto_buf); fscrypt_fname_free_buffer(&fname->crypto_buf);
return ret; return ret;
} }
EXPORT_SYMBOL(fscrypt_setup_filename);
void f2fs_fname_free_filename(struct f2fs_filename *fname) void fscrypt_free_filename(struct fscrypt_name *fname)
{ {
kfree(fname->crypto_buf.name); kfree(fname->crypto_buf.name);
fname->crypto_buf.name = NULL; fname->crypto_buf.name = NULL;
fname->usr_fname = NULL; fname->usr_fname = NULL;
fname->disk_name.name = NULL; fname->disk_name.name = NULL;
} }
EXPORT_SYMBOL(fscrypt_free_filename);

272
fs/crypto/keyinfo.c Normal file
View File

@ -0,0 +1,272 @@
/*
* key management facility for FS encryption support.
*
* Copyright (C) 2015, Google, Inc.
*
* This contains encryption key functions.
*
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
*/
#include <keys/encrypted-type.h>
#include <keys/user-type.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <uapi/linux/keyctl.h>
#include <linux/fscrypto.h>
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
{
struct fscrypt_completion_result *ecr = req->data;
if (rc == -EINPROGRESS)
return;
ecr->res = rc;
complete(&ecr->completion);
}
/**
* 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.
*
* 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])
{
int res = 0;
struct skcipher_request *req = NULL;
DECLARE_FS_COMPLETION_RESULT(ecr);
struct scatterlist src_sg, dst_sg;
struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
if (IS_ERR(tfm)) {
res = PTR_ERR(tfm);
tfm = NULL;
goto out;
}
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
req = skcipher_request_alloc(tfm, GFP_NOFS);
if (!req) {
res = -ENOMEM;
goto out;
}
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
derive_crypt_complete, &ecr);
res = crypto_skcipher_setkey(tfm, 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);
res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) {
wait_for_completion(&ecr.completion);
res = ecr.res;
}
out:
skcipher_request_free(req);
crypto_free_skcipher(tfm);
return res;
}
static void put_crypt_info(struct fscrypt_info *ci)
{
if (!ci)
return;
key_put(ci->ci_keyring_key);
crypto_free_skcipher(ci->ci_ctfm);
kmem_cache_free(fscrypt_info_cachep, ci);
}
int get_crypt_info(struct inode *inode)
{
struct fscrypt_info *crypt_info;
u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE +
(FS_KEY_DESCRIPTOR_SIZE * 2) + 1];
struct key *keyring_key = NULL;
struct fscrypt_key *master_key;
struct fscrypt_context ctx;
const struct user_key_payload *ukp;
struct crypto_skcipher *ctfm;
const char *cipher_str;
u8 raw_key[FS_MAX_KEY_SIZE];
u8 mode;
int res;
res = fscrypt_initialize();
if (res)
return res;
if (!inode->i_sb->s_cop->get_context)
return -EOPNOTSUPP;
retry:
crypt_info = ACCESS_ONCE(inode->i_crypt_info);
if (crypt_info) {
if (!crypt_info->ci_keyring_key ||
key_validate(crypt_info->ci_keyring_key) == 0)
return 0;
fscrypt_put_encryption_info(inode, crypt_info);
goto retry;
}
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
if (res < 0) {
if (!fscrypt_dummy_context_enabled(inode))
return res;
ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
ctx.flags = 0;
} else if (res != sizeof(ctx)) {
return -EINVAL;
}
res = 0;
crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS);
if (!crypt_info)
return -ENOMEM;
crypt_info->ci_flags = ctx.flags;
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_keyring_key = NULL;
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
if (S_ISREG(inode->i_mode))
mode = crypt_info->ci_data_mode;
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
mode = crypt_info->ci_filename_mode;
else
BUG();
switch (mode) {
case FS_ENCRYPTION_MODE_AES_256_XTS:
cipher_str = "xts(aes)";
break;
case FS_ENCRYPTION_MODE_AES_256_CTS:
cipher_str = "cts(cbc(aes))";
break;
default:
printk_once(KERN_WARNING
"%s: unsupported key mode %d (ino %u)\n",
__func__, mode, (unsigned) inode->i_ino);
res = -ENOKEY;
goto out;
}
if (fscrypt_dummy_context_enabled(inode)) {
memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
goto got_key;
}
memcpy(full_key_descriptor, FS_KEY_DESC_PREFIX,
FS_KEY_DESC_PREFIX_SIZE);
sprintf(full_key_descriptor + FS_KEY_DESC_PREFIX_SIZE,
"%*phN", FS_KEY_DESCRIPTOR_SIZE,
ctx.master_key_descriptor);
full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE +
(2 * FS_KEY_DESCRIPTOR_SIZE)] = '\0';
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
if (IS_ERR(keyring_key)) {
res = PTR_ERR(keyring_key);
keyring_key = NULL;
goto out;
}
crypt_info->ci_keyring_key = keyring_key;
if (keyring_key->type != &key_type_logon) {
printk_once(KERN_WARNING
"%s: key type must be logon\n", __func__);
res = -ENOKEY;
goto out;
}
down_read(&keyring_key->sem);
ukp = user_key_payload(keyring_key);
if (ukp->datalen != sizeof(struct fscrypt_key)) {
res = -EINVAL;
up_read(&keyring_key->sem);
goto out;
}
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) {
printk_once(KERN_WARNING
"%s: key size incorrect: %d\n",
__func__, master_key->size);
res = -ENOKEY;
up_read(&keyring_key->sem);
goto out;
}
res = derive_key_aes(ctx.nonce, master_key->raw, raw_key);
up_read(&keyring_key->sem);
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);
goto out;
}
crypt_info->ci_ctfm = ctfm;
crypto_skcipher_clear_flags(ctfm, ~0);
crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
res = crypto_skcipher_setkey(ctfm, raw_key, fscrypt_key_size(mode));
if (res)
goto out;
memzero_explicit(raw_key, sizeof(raw_key));
if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) {
put_crypt_info(crypt_info);
goto retry;
}
return 0;
out:
if (res == -ENOKEY)
res = 0;
put_crypt_info(crypt_info);
memzero_explicit(raw_key, sizeof(raw_key));
return res;
}
void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)
{
struct fscrypt_info *prev;
if (ci == NULL)
ci = ACCESS_ONCE(inode->i_crypt_info);
if (ci == NULL)
return;
prev = cmpxchg(&inode->i_crypt_info, ci, NULL);
if (prev != ci)
return;
put_crypt_info(ci);
}
EXPORT_SYMBOL(fscrypt_put_encryption_info);
int fscrypt_get_encryption_info(struct inode *inode)
{
struct fscrypt_info *ci = inode->i_crypt_info;
if (!ci ||
(ci->ci_keyring_key &&
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED) |
(1 << KEY_FLAG_DEAD)))))
return get_crypt_info(inode);
return 0;
}
EXPORT_SYMBOL(fscrypt_get_encryption_info);

229
fs/crypto/policy.c Normal file
View File

@ -0,0 +1,229 @@
/*
* Encryption policy functions for per-file encryption support.
*
* Copyright (C) 2015, Google, Inc.
* Copyright (C) 2015, Motorola Mobility.
*
* Written by Michael Halcrow, 2015.
* Modified by Jaegeuk Kim, 2015.
*/
#include <linux/random.h>
#include <linux/string.h>
#include <linux/fscrypto.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);
}
/*
* check whether the policy is consistent with the encryption context
* for the inode
*/
static int is_encryption_context_consistent_with_policy(struct inode *inode,
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));
}
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);
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;
ctx.contents_encryption_mode = policy->contents_encryption_mode;
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
ctx.flags = policy->flags;
BUILD_BUG_ON(sizeof(ctx.nonce) != FS_KEY_DERIVATION_NONCE_SIZE);
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL);
}
int fscrypt_process_policy(struct inode *inode,
const struct fscrypt_policy *policy)
{
if (policy->version != 0)
return -EINVAL;
if (!inode_has_encryption_context(inode)) {
if (!inode->i_sb->s_cop->empty_dir)
return -EOPNOTSUPP;
if (!inode->i_sb->s_cop->empty_dir(inode))
return -ENOTEMPTY;
return create_encryption_context_from_policy(inode, policy);
}
if (is_encryption_context_consistent_with_policy(inode, policy))
return 0;
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
__func__);
return -EINVAL;
}
EXPORT_SYMBOL(fscrypt_process_policy);
int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy)
{
struct fscrypt_context ctx;
int res;
if (!inode->i_sb->s_cop->get_context ||
!inode->i_sb->s_cop->is_encrypted(inode))
return -ENODATA;
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
if (res != sizeof(ctx))
return -ENODATA;
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,
FS_KEY_DESCRIPTOR_SIZE);
return 0;
}
EXPORT_SYMBOL(fscrypt_get_policy);
int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
{
struct fscrypt_info *parent_ci, *child_ci;
int res;
if ((parent == NULL) || (child == NULL)) {
printk(KERN_ERR "parent %p child %p\n", parent, child);
BUG_ON(1);
}
/* no restrictions if the parent directory is not encrypted */
if (!parent->i_sb->s_cop->is_encrypted(parent))
return 1;
/* if the child directory is not encrypted, this is always a problem */
if (!parent->i_sb->s_cop->is_encrypted(child))
return 0;
res = fscrypt_get_encryption_info(parent);
if (res)
return 0;
res = fscrypt_get_encryption_info(child);
if (res)
return 0;
parent_ci = parent->i_crypt_info;
child_ci = child->i_crypt_info;
if (!parent_ci && !child_ci)
return 1;
if (!parent_ci || !child_ci)
return 0;
return (memcmp(parent_ci->ci_master_key,
child_ci->ci_master_key,
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
(parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
(parent_ci->ci_flags == child_ci->ci_flags));
}
EXPORT_SYMBOL(fscrypt_has_permitted_context);
/**
* fscrypt_inherit_context() - Sets a child context from its parent
* @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
*
* Return: Zero on success, non-zero otherwise
*/
int fscrypt_inherit_context(struct inode *parent, struct inode *child,
void *fs_data, bool preload)
{
struct fscrypt_context ctx;
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;
ci = parent->i_crypt_info;
if (ci == NULL)
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);
}
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
res = parent->i_sb->s_cop->set_context(child, &ctx,
sizeof(ctx), fs_data);
if (res)
return res;
return preload ? fscrypt_get_encryption_info(child): 0;
}
EXPORT_SYMBOL(fscrypt_inherit_context);

View File

@ -1,6 +1,8 @@
config F2FS_FS config F2FS_FS
tristate "F2FS filesystem support" tristate "F2FS filesystem support"
depends on BLOCK depends on BLOCK
select CRYPTO
select CRYPTO_CRC32
help help
F2FS is based on Log-structured File System (LFS), which supports F2FS is based on Log-structured File System (LFS), which supports
versatile "flash-friendly" features. The design has been focused on versatile "flash-friendly" features. The design has been focused on
@ -76,15 +78,7 @@ config F2FS_FS_ENCRYPTION
bool "F2FS Encryption" bool "F2FS Encryption"
depends on F2FS_FS depends on F2FS_FS
depends on F2FS_FS_XATTR depends on F2FS_FS_XATTR
select CRYPTO_AES select FS_ENCRYPTION
select CRYPTO_CBC
select CRYPTO_ECB
select CRYPTO_XTS
select CRYPTO_CTS
select CRYPTO_CTR
select CRYPTO_SHA256
select KEYS
select ENCRYPTED_KEYS
help help
Enable encryption of f2fs files and directories. This Enable encryption of f2fs files and directories. This
feature is similar to ecryptfs, but it is more memory feature is similar to ecryptfs, but it is more memory

View File

@ -7,5 +7,3 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \
crypto_key.o crypto_fname.o

View File

@ -39,7 +39,7 @@ repeat:
cond_resched(); cond_resched();
goto repeat; goto repeat;
} }
f2fs_wait_on_page_writeback(page, META); f2fs_wait_on_page_writeback(page, META, true);
SetPageUptodate(page); SetPageUptodate(page);
return page; return page;
} }
@ -56,7 +56,8 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index,
.sbi = sbi, .sbi = sbi,
.type = META, .type = META,
.rw = READ_SYNC | REQ_META | REQ_PRIO, .rw = READ_SYNC | REQ_META | REQ_PRIO,
.blk_addr = index, .old_blkaddr = index,
.new_blkaddr = index,
.encrypted_page = NULL, .encrypted_page = NULL,
}; };
@ -143,7 +144,6 @@ bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type)
int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
int type, bool sync) int type, bool sync)
{ {
block_t prev_blk_addr = 0;
struct page *page; struct page *page;
block_t blkno = start; block_t blkno = start;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
@ -152,10 +152,12 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
.rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA, .rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA,
.encrypted_page = NULL, .encrypted_page = NULL,
}; };
struct blk_plug plug;
if (unlikely(type == META_POR)) if (unlikely(type == META_POR))
fio.rw &= ~REQ_META; fio.rw &= ~REQ_META;
blk_start_plug(&plug);
for (; nrpages-- > 0; blkno++) { for (; nrpages-- > 0; blkno++) {
if (!is_valid_blkaddr(sbi, blkno, type)) if (!is_valid_blkaddr(sbi, blkno, type))
@ -167,27 +169,24 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid))) NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid)))
blkno = 0; blkno = 0;
/* get nat block addr */ /* get nat block addr */
fio.blk_addr = current_nat_addr(sbi, fio.new_blkaddr = current_nat_addr(sbi,
blkno * NAT_ENTRY_PER_BLOCK); blkno * NAT_ENTRY_PER_BLOCK);
break; break;
case META_SIT: case META_SIT:
/* get sit block addr */ /* get sit block addr */
fio.blk_addr = current_sit_addr(sbi, fio.new_blkaddr = current_sit_addr(sbi,
blkno * SIT_ENTRY_PER_BLOCK); blkno * SIT_ENTRY_PER_BLOCK);
if (blkno != start && prev_blk_addr + 1 != fio.blk_addr)
goto out;
prev_blk_addr = fio.blk_addr;
break; break;
case META_SSA: case META_SSA:
case META_CP: case META_CP:
case META_POR: case META_POR:
fio.blk_addr = blkno; fio.new_blkaddr = blkno;
break; break;
default: default:
BUG(); BUG();
} }
page = grab_cache_page(META_MAPPING(sbi), fio.blk_addr); page = grab_cache_page(META_MAPPING(sbi), fio.new_blkaddr);
if (!page) if (!page)
continue; continue;
if (PageUptodate(page)) { if (PageUptodate(page)) {
@ -196,11 +195,13 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
} }
fio.page = page; fio.page = page;
fio.old_blkaddr = fio.new_blkaddr;
f2fs_submit_page_mbio(&fio); f2fs_submit_page_mbio(&fio);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
} }
out: out:
f2fs_submit_merged_bio(sbi, META, READ); f2fs_submit_merged_bio(sbi, META, READ);
blk_finish_plug(&plug);
return blkno - start; return blkno - start;
} }
@ -232,13 +233,17 @@ static int f2fs_write_meta_page(struct page *page,
if (unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
goto redirty_out; goto redirty_out;
f2fs_wait_on_page_writeback(page, META);
write_meta_page(sbi, page); write_meta_page(sbi, page);
dec_page_count(sbi, F2FS_DIRTY_META); dec_page_count(sbi, F2FS_DIRTY_META);
if (wbc->for_reclaim)
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE);
unlock_page(page); unlock_page(page);
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, META, WRITE); f2fs_submit_merged_bio(sbi, META, WRITE);
return 0; return 0;
redirty_out: redirty_out:
@ -252,13 +257,13 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
long diff, written; long diff, written;
trace_f2fs_writepages(mapping->host, wbc, META);
/* collect a number of dirty meta pages and write together */ /* collect a number of dirty meta pages and write together */
if (wbc->for_kupdate || if (wbc->for_kupdate ||
get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
goto skip_write; goto skip_write;
trace_f2fs_writepages(mapping->host, wbc, META);
/* if mounting is failed, skip writing node pages */ /* if mounting is failed, skip writing node pages */
mutex_lock(&sbi->cp_mutex); mutex_lock(&sbi->cp_mutex);
diff = nr_pages_to_write(sbi, META, wbc); diff = nr_pages_to_write(sbi, META, wbc);
@ -269,6 +274,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
skip_write: skip_write:
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META); wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META);
trace_f2fs_writepages(mapping->host, wbc, META);
return 0; return 0;
} }
@ -276,15 +282,18 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
long nr_to_write) long nr_to_write)
{ {
struct address_space *mapping = META_MAPPING(sbi); struct address_space *mapping = META_MAPPING(sbi);
pgoff_t index = 0, end = LONG_MAX, prev = LONG_MAX; pgoff_t index = 0, end = ULONG_MAX, prev = ULONG_MAX;
struct pagevec pvec; struct pagevec pvec;
long nwritten = 0; long nwritten = 0;
struct writeback_control wbc = { struct writeback_control wbc = {
.for_reclaim = 0, .for_reclaim = 0,
}; };
struct blk_plug plug;
pagevec_init(&pvec, 0); pagevec_init(&pvec, 0);
blk_start_plug(&plug);
while (index <= end) { while (index <= end) {
int i, nr_pages; int i, nr_pages;
nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
@ -296,7 +305,7 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i]; struct page *page = pvec.pages[i];
if (prev == LONG_MAX) if (prev == ULONG_MAX)
prev = page->index - 1; prev = page->index - 1;
if (nr_to_write != LONG_MAX && page->index != prev + 1) { if (nr_to_write != LONG_MAX && page->index != prev + 1) {
pagevec_release(&pvec); pagevec_release(&pvec);
@ -315,6 +324,9 @@ continue_unlock:
goto continue_unlock; goto continue_unlock;
} }
f2fs_wait_on_page_writeback(page, META, true);
BUG_ON(PageWriteback(page));
if (!clear_page_dirty_for_io(page)) if (!clear_page_dirty_for_io(page))
goto continue_unlock; goto continue_unlock;
@ -334,6 +346,8 @@ stop:
if (nwritten) if (nwritten)
f2fs_submit_merged_bio(sbi, type, WRITE); f2fs_submit_merged_bio(sbi, type, WRITE);
blk_finish_plug(&plug);
return nwritten; return nwritten;
} }
@ -621,7 +635,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
goto invalid_cp1; goto invalid_cp1;
crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset)));
if (!f2fs_crc_valid(crc, cp_block, crc_offset)) if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset))
goto invalid_cp1; goto invalid_cp1;
pre_version = cur_cp_version(cp_block); pre_version = cur_cp_version(cp_block);
@ -636,7 +650,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
goto invalid_cp2; goto invalid_cp2;
crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset)));
if (!f2fs_crc_valid(crc, cp_block, crc_offset)) if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset))
goto invalid_cp2; goto invalid_cp2;
cur_version = cur_cp_version(cp_block); cur_version = cur_cp_version(cp_block);
@ -696,6 +710,10 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
cp_block = (struct f2fs_checkpoint *)page_address(cur_page); cp_block = (struct f2fs_checkpoint *)page_address(cur_page);
memcpy(sbi->ckpt, cp_block, blk_size); memcpy(sbi->ckpt, cp_block, blk_size);
/* Sanity checking of checkpoint */
if (sanity_check_ckpt(sbi))
goto fail_no_cp;
if (cp_blks <= 1) if (cp_blks <= 1)
goto done; goto done;
@ -902,7 +920,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi)
if (!get_pages(sbi, F2FS_WRITEBACK)) if (!get_pages(sbi, F2FS_WRITEBACK))
break; break;
io_schedule(); io_schedule_timeout(5*HZ);
} }
finish_wait(&sbi->cp_wait, &wait); finish_wait(&sbi->cp_wait, &wait);
} }
@ -921,6 +939,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
int cp_payload_blks = __cp_payload(sbi); int cp_payload_blks = __cp_payload(sbi);
block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg); block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg);
bool invalidate = false; bool invalidate = false;
struct super_block *sb = sbi->sb;
struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
u64 kbytes_written;
/* /*
* This avoids to conduct wrong roll-forward operations and uses * This avoids to conduct wrong roll-forward operations and uses
@ -1008,7 +1029,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP));
get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP));
crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset)); crc32 = f2fs_crc32(sbi, ckpt, le32_to_cpu(ckpt->checksum_offset));
*((__le32 *)((unsigned char *)ckpt + *((__le32 *)((unsigned char *)ckpt +
le32_to_cpu(ckpt->checksum_offset))) le32_to_cpu(ckpt->checksum_offset)))
= cpu_to_le32(crc32); = cpu_to_le32(crc32);
@ -1034,6 +1055,14 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
write_data_summaries(sbi, start_blk); write_data_summaries(sbi, start_blk);
start_blk += data_sum_blocks; start_blk += data_sum_blocks;
/* Record write statistics in the hot node summary */
kbytes_written = sbi->kbytes_written;
if (sb->s_bdev->bd_part)
kbytes_written += BD_PART_WRITTEN(sbi);
seg_i->journal->info.kbytes_written = cpu_to_le64(kbytes_written);
if (__remain_node_summaries(cpc->reason)) { if (__remain_node_summaries(cpc->reason)) {
write_node_summaries(sbi, start_blk); write_node_summaries(sbi, start_blk);
start_blk += NR_CURSEG_NODE_TYPE; start_blk += NR_CURSEG_NODE_TYPE;
@ -1048,8 +1077,8 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
if (unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
return -EIO; return -EIO;
filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX); filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LLONG_MAX);
filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX); filemap_fdatawait_range(META_MAPPING(sbi), 0, LLONG_MAX);
/* update user_block_counts */ /* update user_block_counts */
sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->last_valid_block_count = sbi->total_valid_block_count;
@ -1112,9 +1141,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops"); trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");
f2fs_submit_merged_bio(sbi, DATA, WRITE); f2fs_flush_merged_bios(sbi);
f2fs_submit_merged_bio(sbi, NODE, WRITE);
f2fs_submit_merged_bio(sbi, META, WRITE);
/* /*
* update checkpoint pack index * update checkpoint pack index

View File

@ -1,489 +0,0 @@
/*
* linux/fs/f2fs/crypto.c
*
* Copied from linux/fs/ext4/crypto.c
*
* Copyright (C) 2015, Google, Inc.
* Copyright (C) 2015, Motorola Mobility
*
* This contains encryption functions for f2fs
*
* Written by Michael Halcrow, 2014.
*
* Filename encryption additions
* Uday Savagaonkar, 2014
* Encryption policy handling additions
* Ildar Muslukhov, 2014
* Remove ext4_encrypted_zeroout(),
* add f2fs_restore_and_release_control_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 <crypto/skcipher.h>
#include <keys/user-type.h>
#include <keys/encrypted-type.h>
#include <linux/ecryptfs.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/key.h>
#include <linux/list.h>
#include <linux/mempool.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/spinlock_types.h>
#include <linux/f2fs_fs.h>
#include <linux/ratelimit.h>
#include <linux/bio.h>
#include "f2fs.h"
#include "xattr.h"
/* Encryption added and removed here! (L: */
static unsigned int num_prealloc_crypto_pages = 32;
static unsigned int num_prealloc_crypto_ctxs = 128;
module_param(num_prealloc_crypto_pages, uint, 0444);
MODULE_PARM_DESC(num_prealloc_crypto_pages,
"Number of crypto pages to preallocate");
module_param(num_prealloc_crypto_ctxs, uint, 0444);
MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
"Number of crypto contexts to preallocate");
static mempool_t *f2fs_bounce_page_pool;
static LIST_HEAD(f2fs_free_crypto_ctxs);
static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock);
static struct workqueue_struct *f2fs_read_workqueue;
static DEFINE_MUTEX(crypto_init);
static struct kmem_cache *f2fs_crypto_ctx_cachep;
struct kmem_cache *f2fs_crypt_info_cachep;
/**
* f2fs_release_crypto_ctx() - Releases an encryption context
* @ctx: The encryption context to release.
*
* If the encryption context was allocated from the pre-allocated pool, returns
* it to that pool. Else, frees it.
*
* If there's a bounce page in the context, this frees that.
*/
void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx)
{
unsigned long flags;
if (ctx->flags & F2FS_WRITE_PATH_FL && ctx->w.bounce_page) {
mempool_free(ctx->w.bounce_page, f2fs_bounce_page_pool);
ctx->w.bounce_page = NULL;
}
ctx->w.control_page = NULL;
if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
kmem_cache_free(f2fs_crypto_ctx_cachep, ctx);
} else {
spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
}
}
/**
* f2fs_get_crypto_ctx() - Gets an encryption context
* @inode: The inode for which we are doing the crypto
*
* Allocates and initializes an encryption context.
*
* Return: An allocated and initialized encryption context on success; error
* value or NULL otherwise.
*/
struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
{
struct f2fs_crypto_ctx *ctx = NULL;
unsigned long flags;
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
if (ci == NULL)
return ERR_PTR(-ENOKEY);
/*
* We first try getting the ctx from a free list because in
* the common case the ctx will have an allocated and
* initialized crypto tfm, so it's probably a worthwhile
* optimization. For the bounce page, we first try getting it
* from the kernel allocator because that's just about as fast
* as getting it from a list and because a cache of free pages
* should generally be a "last resort" option for a filesystem
* to be able to do its job.
*/
spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
ctx = list_first_entry_or_null(&f2fs_free_crypto_ctxs,
struct f2fs_crypto_ctx, free_list);
if (ctx)
list_del(&ctx->free_list);
spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
if (!ctx) {
ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_NOFS);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
} else {
ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
}
ctx->flags &= ~F2FS_WRITE_PATH_FL;
return ctx;
}
/*
* Call f2fs_decrypt on every single page, reusing the encryption
* context.
*/
static void completion_pages(struct work_struct *work)
{
struct f2fs_crypto_ctx *ctx =
container_of(work, struct f2fs_crypto_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 = f2fs_decrypt(ctx, page);
if (ret) {
WARN_ON_ONCE(1);
SetPageError(page);
} else
SetPageUptodate(page);
unlock_page(page);
}
f2fs_release_crypto_ctx(ctx);
bio_put(bio);
}
void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio)
{
INIT_WORK(&ctx->r.work, completion_pages);
ctx->r.bio = bio;
queue_work(f2fs_read_workqueue, &ctx->r.work);
}
static void f2fs_crypto_destroy(void)
{
struct f2fs_crypto_ctx *pos, *n;
list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list)
kmem_cache_free(f2fs_crypto_ctx_cachep, pos);
INIT_LIST_HEAD(&f2fs_free_crypto_ctxs);
if (f2fs_bounce_page_pool)
mempool_destroy(f2fs_bounce_page_pool);
f2fs_bounce_page_pool = NULL;
}
/**
* f2fs_crypto_initialize() - Set up for f2fs encryption.
*
* 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 f2fs_crypto_initialize(void)
{
int i, res = -ENOMEM;
if (f2fs_bounce_page_pool)
return 0;
mutex_lock(&crypto_init);
if (f2fs_bounce_page_pool)
goto already_initialized;
for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
struct f2fs_crypto_ctx *ctx;
ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_KERNEL);
if (!ctx)
goto fail;
list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
}
/* must be allocated at the last step to avoid race condition above */
f2fs_bounce_page_pool =
mempool_create_page_pool(num_prealloc_crypto_pages, 0);
if (!f2fs_bounce_page_pool)
goto fail;
already_initialized:
mutex_unlock(&crypto_init);
return 0;
fail:
f2fs_crypto_destroy();
mutex_unlock(&crypto_init);
return res;
}
/**
* f2fs_exit_crypto() - Shutdown the f2fs encryption system
*/
void f2fs_exit_crypto(void)
{
f2fs_crypto_destroy();
if (f2fs_read_workqueue)
destroy_workqueue(f2fs_read_workqueue);
if (f2fs_crypto_ctx_cachep)
kmem_cache_destroy(f2fs_crypto_ctx_cachep);
if (f2fs_crypt_info_cachep)
kmem_cache_destroy(f2fs_crypt_info_cachep);
}
int __init f2fs_init_crypto(void)
{
int res = -ENOMEM;
f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0);
if (!f2fs_read_workqueue)
goto fail;
f2fs_crypto_ctx_cachep = KMEM_CACHE(f2fs_crypto_ctx,
SLAB_RECLAIM_ACCOUNT);
if (!f2fs_crypto_ctx_cachep)
goto fail;
f2fs_crypt_info_cachep = KMEM_CACHE(f2fs_crypt_info,
SLAB_RECLAIM_ACCOUNT);
if (!f2fs_crypt_info_cachep)
goto fail;
return 0;
fail:
f2fs_exit_crypto();
return res;
}
void f2fs_restore_and_release_control_page(struct page **page)
{
struct f2fs_crypto_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 f2fs_crypto_ctx *)page_private(bounce_page);
/* restore control page */
*page = ctx->w.control_page;
f2fs_restore_control_page(bounce_page);
}
void f2fs_restore_control_page(struct page *data_page)
{
struct f2fs_crypto_ctx *ctx =
(struct f2fs_crypto_ctx *)page_private(data_page);
set_page_private(data_page, (unsigned long)NULL);
ClearPagePrivate(data_page);
unlock_page(data_page);
f2fs_release_crypto_ctx(ctx);
}
/**
* f2fs_crypt_complete() - The completion callback for page encryption
* @req: The asynchronous encryption request context
* @res: The result of the encryption operation
*/
static void f2fs_crypt_complete(struct crypto_async_request *req, int res)
{
struct f2fs_completion_result *ecr = req->data;
if (res == -EINPROGRESS)
return;
ecr->res = res;
complete(&ecr->completion);
}
typedef enum {
F2FS_DECRYPT = 0,
F2FS_ENCRYPT,
} f2fs_direction_t;
static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx,
struct inode *inode,
f2fs_direction_t rw,
pgoff_t index,
struct page *src_page,
struct page *dest_page)
{
u8 xts_tweak[F2FS_XTS_TWEAK_SIZE];
struct skcipher_request *req = NULL;
DECLARE_F2FS_COMPLETION_RESULT(ecr);
struct scatterlist dst, src;
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
struct crypto_skcipher *tfm = ci->ci_ctfm;
int res = 0;
req = skcipher_request_alloc(tfm, GFP_NOFS);
if (!req) {
printk_ratelimited(KERN_ERR
"%s: crypto_request_alloc() failed\n",
__func__);
return -ENOMEM;
}
skcipher_request_set_callback(
req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
f2fs_crypt_complete, &ecr);
BUILD_BUG_ON(F2FS_XTS_TWEAK_SIZE < sizeof(index));
memcpy(xts_tweak, &index, sizeof(index));
memset(&xts_tweak[sizeof(index)], 0,
F2FS_XTS_TWEAK_SIZE - sizeof(index));
sg_init_table(&dst, 1);
sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0);
sg_init_table(&src, 1);
sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0);
skcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
xts_tweak);
if (rw == F2FS_DECRYPT)
res = crypto_skcipher_decrypt(req);
else
res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) {
BUG_ON(req->base.data != &ecr);
wait_for_completion(&ecr.completion);
res = ecr.res;
}
skcipher_request_free(req);
if (res) {
printk_ratelimited(KERN_ERR
"%s: crypto_skcipher_encrypt() returned %d\n",
__func__, res);
return res;
}
return 0;
}
static struct page *alloc_bounce_page(struct f2fs_crypto_ctx *ctx)
{
ctx->w.bounce_page = mempool_alloc(f2fs_bounce_page_pool, GFP_NOWAIT);
if (ctx->w.bounce_page == NULL)
return ERR_PTR(-ENOMEM);
ctx->flags |= F2FS_WRITE_PATH_FL;
return ctx->w.bounce_page;
}
/**
* f2fs_encrypt() - Encrypts a page
* @inode: The inode for which the encryption should take place
* @plaintext_page: The page to encrypt. Must be locked.
*
* Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
* encryption context.
*
* Called on the page write path. The caller must call
* f2fs_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
* error value or NULL.
*/
struct page *f2fs_encrypt(struct inode *inode,
struct page *plaintext_page)
{
struct f2fs_crypto_ctx *ctx;
struct page *ciphertext_page = NULL;
int err;
BUG_ON(!PageLocked(plaintext_page));
ctx = f2fs_get_crypto_ctx(inode);
if (IS_ERR(ctx))
return (struct page *)ctx;
/* The encryption operation will require a bounce page. */
ciphertext_page = alloc_bounce_page(ctx);
if (IS_ERR(ciphertext_page))
goto err_out;
ctx->w.control_page = plaintext_page;
err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index,
plaintext_page, ciphertext_page);
if (err) {
ciphertext_page = ERR_PTR(err);
goto err_out;
}
SetPagePrivate(ciphertext_page);
set_page_private(ciphertext_page, (unsigned long)ctx);
lock_page(ciphertext_page);
return ciphertext_page;
err_out:
f2fs_release_crypto_ctx(ctx);
return ciphertext_page;
}
/**
* f2fs_decrypt() - Decrypts a page in-place
* @ctx: The encryption context.
* @page: The page to decrypt. Must be locked.
*
* Decrypts page in-place using the ctx encryption context.
*
* Called from the read completion callback.
*
* Return: Zero on success, non-zero otherwise.
*/
int f2fs_decrypt(struct f2fs_crypto_ctx *ctx, struct page *page)
{
BUG_ON(!PageLocked(page));
return f2fs_page_crypto(ctx, page->mapping->host,
F2FS_DECRYPT, page->index, page, page);
}
/*
* Convenience function which takes care of allocating and
* deallocating the encryption context
*/
int f2fs_decrypt_one(struct inode *inode, struct page *page)
{
struct f2fs_crypto_ctx *ctx = f2fs_get_crypto_ctx(inode);
int ret;
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ret = f2fs_decrypt(ctx, page);
f2fs_release_crypto_ctx(ctx);
return ret;
}
bool f2fs_valid_contents_enc_mode(uint32_t mode)
{
return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS);
}
/**
* f2fs_validate_encryption_key_size() - Validate the encryption key size
* @mode: The key mode.
* @size: The key size to validate.
*
* Return: The validated key size for @mode. Zero if invalid.
*/
uint32_t f2fs_validate_encryption_key_size(uint32_t mode, uint32_t size)
{
if (size == f2fs_encryption_key_size(mode))
return size;
return 0;
}

View File

@ -1,250 +0,0 @@
/*
* linux/fs/f2fs/crypto_key.c
*
* Copied from linux/fs/f2fs/crypto_key.c
*
* Copyright (C) 2015, Google, Inc.
*
* This contains encryption key functions for f2fs
*
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
*/
#include <keys/encrypted-type.h>
#include <keys/user-type.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <uapi/linux/keyctl.h>
#include <crypto/skcipher.h>
#include <linux/f2fs_fs.h>
#include "f2fs.h"
#include "xattr.h"
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
{
struct f2fs_completion_result *ecr = req->data;
if (rc == -EINPROGRESS)
return;
ecr->res = rc;
complete(&ecr->completion);
}
/**
* f2fs_derive_key_aes() - Derive a key using AES-128-ECB
* @deriving_key: Encryption key used for derivatio.
* @source_key: Source key to which to apply derivation.
* @derived_key: Derived key.
*
* Return: Zero on success; non-zero otherwise.
*/
static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE],
char source_key[F2FS_AES_256_XTS_KEY_SIZE],
char derived_key[F2FS_AES_256_XTS_KEY_SIZE])
{
int res = 0;
struct skcipher_request *req = NULL;
DECLARE_F2FS_COMPLETION_RESULT(ecr);
struct scatterlist src_sg, dst_sg;
struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
if (IS_ERR(tfm)) {
res = PTR_ERR(tfm);
tfm = NULL;
goto out;
}
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
req = skcipher_request_alloc(tfm, GFP_NOFS);
if (!req) {
res = -ENOMEM;
goto out;
}
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
derive_crypt_complete, &ecr);
res = crypto_skcipher_setkey(tfm, deriving_key,
F2FS_AES_128_ECB_KEY_SIZE);
if (res < 0)
goto out;
sg_init_one(&src_sg, source_key, F2FS_AES_256_XTS_KEY_SIZE);
sg_init_one(&dst_sg, derived_key, F2FS_AES_256_XTS_KEY_SIZE);
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
F2FS_AES_256_XTS_KEY_SIZE, NULL);
res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) {
BUG_ON(req->base.data != &ecr);
wait_for_completion(&ecr.completion);
res = ecr.res;
}
out:
skcipher_request_free(req);
crypto_free_skcipher(tfm);
return res;
}
static void f2fs_free_crypt_info(struct f2fs_crypt_info *ci)
{
if (!ci)
return;
key_put(ci->ci_keyring_key);
crypto_free_skcipher(ci->ci_ctfm);
kmem_cache_free(f2fs_crypt_info_cachep, ci);
}
void f2fs_free_encryption_info(struct inode *inode, struct f2fs_crypt_info *ci)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_crypt_info *prev;
if (ci == NULL)
ci = ACCESS_ONCE(fi->i_crypt_info);
if (ci == NULL)
return;
prev = cmpxchg(&fi->i_crypt_info, ci, NULL);
if (prev != ci)
return;
f2fs_free_crypt_info(ci);
}
int _f2fs_get_encryption_info(struct inode *inode)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_crypt_info *crypt_info;
char full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE +
(F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1];
struct key *keyring_key = NULL;
struct f2fs_encryption_key *master_key;
struct f2fs_encryption_context ctx;
const struct user_key_payload *ukp;
struct crypto_skcipher *ctfm;
const char *cipher_str;
char raw_key[F2FS_MAX_KEY_SIZE];
char mode;
int res;
res = f2fs_crypto_initialize();
if (res)
return res;
retry:
crypt_info = ACCESS_ONCE(fi->i_crypt_info);
if (crypt_info) {
if (!crypt_info->ci_keyring_key ||
key_validate(crypt_info->ci_keyring_key) == 0)
return 0;
f2fs_free_encryption_info(inode, crypt_info);
goto retry;
}
res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
&ctx, sizeof(ctx), NULL);
if (res < 0)
return res;
else if (res != sizeof(ctx))
return -EINVAL;
res = 0;
crypt_info = kmem_cache_alloc(f2fs_crypt_info_cachep, GFP_NOFS);
if (!crypt_info)
return -ENOMEM;
crypt_info->ci_flags = ctx.flags;
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_keyring_key = NULL;
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
if (S_ISREG(inode->i_mode))
mode = crypt_info->ci_data_mode;
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
mode = crypt_info->ci_filename_mode;
else
BUG();
switch (mode) {
case F2FS_ENCRYPTION_MODE_AES_256_XTS:
cipher_str = "xts(aes)";
break;
case F2FS_ENCRYPTION_MODE_AES_256_CTS:
cipher_str = "cts(cbc(aes))";
break;
default:
printk_once(KERN_WARNING
"f2fs: unsupported key mode %d (ino %u)\n",
mode, (unsigned) inode->i_ino);
res = -ENOKEY;
goto out;
}
memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX,
F2FS_KEY_DESC_PREFIX_SIZE);
sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE,
"%*phN", F2FS_KEY_DESCRIPTOR_SIZE,
ctx.master_key_descriptor);
full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE +
(2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0';
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
if (IS_ERR(keyring_key)) {
res = PTR_ERR(keyring_key);
keyring_key = NULL;
goto out;
}
crypt_info->ci_keyring_key = keyring_key;
BUG_ON(keyring_key->type != &key_type_logon);
ukp = user_key_payload(keyring_key);
if (ukp->datalen != sizeof(struct f2fs_encryption_key)) {
res = -EINVAL;
goto out;
}
master_key = (struct f2fs_encryption_key *)ukp->data;
BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE !=
F2FS_KEY_DERIVATION_NONCE_SIZE);
BUG_ON(master_key->size != F2FS_AES_256_XTS_KEY_SIZE);
res = f2fs_derive_key_aes(ctx.nonce, master_key->raw,
raw_key);
if (res)
goto out;
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);
goto out;
}
crypt_info->ci_ctfm = ctfm;
crypto_skcipher_clear_flags(ctfm, ~0);
crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
res = crypto_skcipher_setkey(ctfm, raw_key,
f2fs_encryption_key_size(mode));
if (res)
goto out;
memzero_explicit(raw_key, sizeof(raw_key));
if (cmpxchg(&fi->i_crypt_info, NULL, crypt_info) != NULL) {
f2fs_free_crypt_info(crypt_info);
goto retry;
}
return 0;
out:
if (res == -ENOKEY && !S_ISREG(inode->i_mode))
res = 0;
f2fs_free_crypt_info(crypt_info);
memzero_explicit(raw_key, sizeof(raw_key));
return res;
}
int f2fs_has_encryption_key(struct inode *inode)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
return (fi->i_crypt_info != NULL);
}

View File

@ -1,209 +0,0 @@
/*
* copied from linux/fs/ext4/crypto_policy.c
*
* Copyright (C) 2015, Google, Inc.
* Copyright (C) 2015, Motorola Mobility.
*
* This contains encryption policy functions for f2fs with some modifications
* to support f2fs-specific xattr APIs.
*
* Written by Michael Halcrow, 2015.
* Modified by Jaegeuk Kim, 2015.
*/
#include <linux/random.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/f2fs_fs.h>
#include "f2fs.h"
#include "xattr.h"
static int f2fs_inode_has_encryption_context(struct inode *inode)
{
int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0, NULL);
return (res > 0);
}
/*
* check whether the policy is consistent with the encryption context
* for the inode
*/
static int f2fs_is_encryption_context_consistent_with_policy(
struct inode *inode, const struct f2fs_encryption_policy *policy)
{
struct f2fs_encryption_context ctx;
int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
sizeof(ctx), NULL);
if (res != sizeof(ctx))
return 0;
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
F2FS_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 f2fs_create_encryption_context_from_policy(
struct inode *inode, const struct f2fs_encryption_policy *policy)
{
struct f2fs_encryption_context ctx;
ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1;
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
F2FS_KEY_DESCRIPTOR_SIZE);
if (!f2fs_valid_contents_enc_mode(policy->contents_encryption_mode)) {
printk(KERN_WARNING
"%s: Invalid contents encryption mode %d\n", __func__,
policy->contents_encryption_mode);
return -EINVAL;
}
if (!f2fs_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 & ~F2FS_POLICY_FLAGS_VALID)
return -EINVAL;
ctx.contents_encryption_mode = policy->contents_encryption_mode;
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
ctx.flags = policy->flags;
BUILD_BUG_ON(sizeof(ctx.nonce) != F2FS_KEY_DERIVATION_NONCE_SIZE);
get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE);
return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
sizeof(ctx), NULL, XATTR_CREATE);
}
int f2fs_process_policy(const struct f2fs_encryption_policy *policy,
struct inode *inode)
{
if (policy->version != 0)
return -EINVAL;
if (!S_ISDIR(inode->i_mode))
return -EINVAL;
if (!f2fs_inode_has_encryption_context(inode)) {
if (!f2fs_empty_dir(inode))
return -ENOTEMPTY;
return f2fs_create_encryption_context_from_policy(inode,
policy);
}
if (f2fs_is_encryption_context_consistent_with_policy(inode, policy))
return 0;
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
__func__);
return -EINVAL;
}
int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy)
{
struct f2fs_encryption_context ctx;
int res;
if (!f2fs_encrypted_inode(inode))
return -ENODATA;
res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
&ctx, sizeof(ctx), NULL);
if (res != sizeof(ctx))
return -ENODATA;
if (ctx.format != F2FS_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,
F2FS_KEY_DESCRIPTOR_SIZE);
return 0;
}
int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
struct inode *child)
{
struct f2fs_crypt_info *parent_ci, *child_ci;
int res;
if ((parent == NULL) || (child == NULL)) {
pr_err("parent %p child %p\n", parent, child);
BUG_ON(1);
}
/* no restrictions if the parent directory is not encrypted */
if (!f2fs_encrypted_inode(parent))
return 1;
/* if the child directory is not encrypted, this is always a problem */
if (!f2fs_encrypted_inode(child))
return 0;
res = f2fs_get_encryption_info(parent);
if (res)
return 0;
res = f2fs_get_encryption_info(child);
if (res)
return 0;
parent_ci = F2FS_I(parent)->i_crypt_info;
child_ci = F2FS_I(child)->i_crypt_info;
if (!parent_ci && !child_ci)
return 1;
if (!parent_ci || !child_ci)
return 0;
return (memcmp(parent_ci->ci_master_key,
child_ci->ci_master_key,
F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
(parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
(parent_ci->ci_flags == child_ci->ci_flags));
}
/**
* f2fs_inherit_context() - Sets a child context from its parent
* @parent: Parent inode from which the context is inherited.
* @child: Child inode that inherits the context from @parent.
*
* Return: Zero on success, non-zero otherwise
*/
int f2fs_inherit_context(struct inode *parent, struct inode *child,
struct page *ipage)
{
struct f2fs_encryption_context ctx;
struct f2fs_crypt_info *ci;
int res;
res = f2fs_get_encryption_info(parent);
if (res < 0)
return res;
ci = F2FS_I(parent)->i_crypt_info;
BUG_ON(ci == NULL);
ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1;
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,
F2FS_KEY_DESCRIPTOR_SIZE);
get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE);
return f2fs_setxattr(child, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
sizeof(ctx), ipage, XATTR_CREATE);
}

View File

@ -34,9 +34,9 @@ static void f2fs_read_end_io(struct bio *bio)
if (f2fs_bio_encrypted(bio)) { if (f2fs_bio_encrypted(bio)) {
if (bio->bi_error) { if (bio->bi_error) {
f2fs_release_crypto_ctx(bio->bi_private); fscrypt_release_ctx(bio->bi_private);
} else { } else {
f2fs_end_io_crypto_work(bio->bi_private, bio); fscrypt_decrypt_bio_pages(bio->bi_private, bio);
return; return;
} }
} }
@ -64,10 +64,9 @@ static void f2fs_write_end_io(struct bio *bio)
bio_for_each_segment_all(bvec, bio, i) { bio_for_each_segment_all(bvec, bio, i) {
struct page *page = bvec->bv_page; struct page *page = bvec->bv_page;
f2fs_restore_and_release_control_page(&page); fscrypt_pullback_bio_page(&page, true);
if (unlikely(bio->bi_error)) { if (unlikely(bio->bi_error)) {
set_page_dirty(page);
set_bit(AS_EIO, &page->mapping->flags); set_bit(AS_EIO, &page->mapping->flags);
f2fs_stop_checkpoint(sbi); f2fs_stop_checkpoint(sbi);
} }
@ -75,8 +74,7 @@ static void f2fs_write_end_io(struct bio *bio)
dec_page_count(sbi, F2FS_WRITEBACK); dec_page_count(sbi, F2FS_WRITEBACK);
} }
if (!get_pages(sbi, F2FS_WRITEBACK) && if (!get_pages(sbi, F2FS_WRITEBACK) && wq_has_sleeper(&sbi->cp_wait))
!list_empty(&sbi->cp_wait.task_list))
wake_up(&sbi->cp_wait); wake_up(&sbi->cp_wait);
bio_put(bio); bio_put(bio);
@ -116,8 +114,54 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
io->bio = NULL; io->bio = NULL;
} }
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode,
enum page_type type, int rw) struct page *page, nid_t ino)
{
struct bio_vec *bvec;
struct page *target;
int i;
if (!io->bio)
return false;
if (!inode && !page && !ino)
return true;
bio_for_each_segment_all(bvec, io->bio, i) {
if (bvec->bv_page->mapping)
target = bvec->bv_page;
else
target = fscrypt_control_page(bvec->bv_page);
if (inode && inode == target->mapping->host)
return true;
if (page && page == target)
return true;
if (ino && ino == ino_of_node(target))
return true;
}
return false;
}
static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode,
struct page *page, nid_t ino,
enum page_type type)
{
enum page_type btype = PAGE_TYPE_OF_BIO(type);
struct f2fs_bio_info *io = &sbi->write_io[btype];
bool ret;
down_read(&io->io_rwsem);
ret = __has_merged_page(io, inode, page, ino);
up_read(&io->io_rwsem);
return ret;
}
static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
struct inode *inode, struct page *page,
nid_t ino, enum page_type type, int rw)
{ {
enum page_type btype = PAGE_TYPE_OF_BIO(type); enum page_type btype = PAGE_TYPE_OF_BIO(type);
struct f2fs_bio_info *io; struct f2fs_bio_info *io;
@ -126,6 +170,9 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
down_write(&io->io_rwsem); down_write(&io->io_rwsem);
if (!__has_merged_page(io, inode, page, ino))
goto out;
/* change META to META_FLUSH in the checkpoint procedure */ /* change META to META_FLUSH in the checkpoint procedure */
if (type >= META_FLUSH) { if (type >= META_FLUSH) {
io->fio.type = META_FLUSH; io->fio.type = META_FLUSH;
@ -135,9 +182,31 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO; io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO;
} }
__submit_merged_bio(io); __submit_merged_bio(io);
out:
up_write(&io->io_rwsem); up_write(&io->io_rwsem);
} }
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type,
int rw)
{
__f2fs_submit_merged_bio(sbi, NULL, NULL, 0, type, rw);
}
void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi,
struct inode *inode, struct page *page,
nid_t ino, enum page_type type, int rw)
{
if (has_merged_page(sbi, inode, page, ino, type))
__f2fs_submit_merged_bio(sbi, inode, page, ino, type, rw);
}
void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi)
{
f2fs_submit_merged_bio(sbi, DATA, WRITE);
f2fs_submit_merged_bio(sbi, NODE, WRITE);
f2fs_submit_merged_bio(sbi, META, WRITE);
}
/* /*
* Fill the locked page with data located in the block address. * Fill the locked page with data located in the block address.
* Return unlocked page. * Return unlocked page.
@ -145,13 +214,14 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
int f2fs_submit_page_bio(struct f2fs_io_info *fio) int f2fs_submit_page_bio(struct f2fs_io_info *fio)
{ {
struct bio *bio; struct bio *bio;
struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page; struct page *page = fio->encrypted_page ?
fio->encrypted_page : fio->page;
trace_f2fs_submit_page_bio(page, fio); trace_f2fs_submit_page_bio(page, fio);
f2fs_trace_ios(fio, 0); f2fs_trace_ios(fio, 0);
/* Allocate a new bio */ /* Allocate a new bio */
bio = __bio_alloc(fio->sbi, fio->blk_addr, 1, is_read_io(fio->rw)); bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->rw));
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
bio_put(bio); bio_put(bio);
@ -172,21 +242,24 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio)
io = is_read ? &sbi->read_io : &sbi->write_io[btype]; io = is_read ? &sbi->read_io : &sbi->write_io[btype];
verify_block_addr(sbi, fio->blk_addr); if (fio->old_blkaddr != NEW_ADDR)
verify_block_addr(sbi, fio->old_blkaddr);
verify_block_addr(sbi, fio->new_blkaddr);
down_write(&io->io_rwsem); down_write(&io->io_rwsem);
if (!is_read) if (!is_read)
inc_page_count(sbi, F2FS_WRITEBACK); inc_page_count(sbi, F2FS_WRITEBACK);
if (io->bio && (io->last_block_in_bio != fio->blk_addr - 1 || if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 ||
io->fio.rw != fio->rw)) io->fio.rw != fio->rw))
__submit_merged_bio(io); __submit_merged_bio(io);
alloc_new: alloc_new:
if (io->bio == NULL) { if (io->bio == NULL) {
int bio_blocks = MAX_BIO_BLOCKS(sbi); int bio_blocks = MAX_BIO_BLOCKS(sbi);
io->bio = __bio_alloc(sbi, fio->blk_addr, bio_blocks, is_read); io->bio = __bio_alloc(sbi, fio->new_blkaddr,
bio_blocks, is_read);
io->fio = *fio; io->fio = *fio;
} }
@ -198,7 +271,7 @@ alloc_new:
goto alloc_new; goto alloc_new;
} }
io->last_block_in_bio = fio->blk_addr; io->last_block_in_bio = fio->new_blkaddr;
f2fs_trace_ios(fio, 0); f2fs_trace_ios(fio, 0);
up_write(&io->io_rwsem); up_write(&io->io_rwsem);
@ -218,7 +291,7 @@ void set_data_blkaddr(struct dnode_of_data *dn)
struct page *node_page = dn->node_page; struct page *node_page = dn->node_page;
unsigned int ofs_in_node = dn->ofs_in_node; unsigned int ofs_in_node = dn->ofs_in_node;
f2fs_wait_on_page_writeback(node_page, NODE); f2fs_wait_on_page_writeback(node_page, NODE, true);
rn = F2FS_NODE(node_page); rn = F2FS_NODE(node_page);
@ -229,6 +302,13 @@ void set_data_blkaddr(struct dnode_of_data *dn)
dn->node_changed = true; dn->node_changed = true;
} }
void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
{
dn->data_blkaddr = blkaddr;
set_data_blkaddr(dn);
f2fs_update_extent_cache(dn);
}
int reserve_new_block(struct dnode_of_data *dn) int reserve_new_block(struct dnode_of_data *dn)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
@ -332,7 +412,7 @@ got_it:
return page; return page;
} }
fio.blk_addr = dn.data_blkaddr; fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
fio.page = page; fio.page = page;
err = f2fs_submit_page_bio(&fio); err = f2fs_submit_page_bio(&fio);
if (err) if (err)
@ -461,7 +541,6 @@ got_it:
static int __allocate_data_block(struct dnode_of_data *dn) static int __allocate_data_block(struct dnode_of_data *dn)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
struct f2fs_inode_info *fi = F2FS_I(dn->inode);
struct f2fs_summary sum; struct f2fs_summary sum;
struct node_info ni; struct node_info ni;
int seg = CURSEG_WARM_DATA; int seg = CURSEG_WARM_DATA;
@ -489,7 +568,7 @@ alloc:
set_data_blkaddr(dn); set_data_blkaddr(dn);
/* update i_size */ /* update i_size */
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) +
dn->ofs_in_node; dn->ofs_in_node;
if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)) if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT))
i_size_write(dn->inode, i_size_write(dn->inode,
@ -497,67 +576,33 @@ alloc:
return 0; return 0;
} }
static int __allocate_data_blocks(struct inode *inode, loff_t offset, ssize_t f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from)
size_t count)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct inode *inode = file_inode(iocb->ki_filp);
struct dnode_of_data dn; struct f2fs_map_blocks map;
u64 start = F2FS_BYTES_TO_BLK(offset); ssize_t ret = 0;
u64 len = F2FS_BYTES_TO_BLK(count);
bool allocated;
u64 end_offset;
int err = 0;
while (len) { map.m_lblk = F2FS_BYTES_TO_BLK(iocb->ki_pos);
f2fs_lock_op(sbi); map.m_len = F2FS_BLK_ALIGN(iov_iter_count(from));
map.m_next_pgofs = NULL;
/* When reading holes, we need its node page */ if (f2fs_encrypted_inode(inode))
set_new_dnode(&dn, inode, NULL, NULL, 0); return 0;
err = get_dnode_of_data(&dn, start, ALLOC_NODE);
if (err)
goto out;
allocated = false; if (iocb->ki_flags & IOCB_DIRECT) {
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); ret = f2fs_convert_inline_inode(inode);
if (ret)
while (dn.ofs_in_node < end_offset && len) { return ret;
block_t blkaddr; return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO);
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto sync_out;
}
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
err = __allocate_data_block(&dn);
if (err)
goto sync_out;
allocated = true;
}
len--;
start++;
dn.ofs_in_node++;
}
if (allocated)
sync_inode_page(&dn);
f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi);
f2fs_balance_fs(sbi, dn.node_changed);
} }
return err; if (iocb->ki_pos + iov_iter_count(from) > MAX_INLINE_DATA) {
ret = f2fs_convert_inline_inode(inode);
sync_out: if (ret)
if (allocated) return ret;
sync_inode_page(&dn); }
f2fs_put_dnode(&dn); if (!f2fs_has_inline_data(inode))
out: return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO);
f2fs_unlock_op(sbi); return ret;
f2fs_balance_fs(sbi, dn.node_changed);
return err;
} }
/* /*
@ -588,13 +633,14 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
/* it only supports block size == page size */ /* it only supports block size == page size */
pgofs = (pgoff_t)map->m_lblk; pgofs = (pgoff_t)map->m_lblk;
if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) { if (!create && f2fs_lookup_extent_cache(inode, pgofs, &ei)) {
map->m_pblk = ei.blk + pgofs - ei.fofs; map->m_pblk = ei.blk + pgofs - ei.fofs;
map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs); map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs);
map->m_flags = F2FS_MAP_MAPPED; map->m_flags = F2FS_MAP_MAPPED;
goto out; goto out;
} }
next_dnode:
if (create) if (create)
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
@ -602,74 +648,18 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, pgofs, mode); err = get_dnode_of_data(&dn, pgofs, mode);
if (err) { if (err) {
if (err == -ENOENT) if (err == -ENOENT) {
err = 0; err = 0;
if (map->m_next_pgofs)
*map->m_next_pgofs =
get_next_page_offset(&dn, pgofs);
}
goto unlock_out; goto unlock_out;
} }
if (dn.data_blkaddr == NEW_ADDR || dn.data_blkaddr == NULL_ADDR) { end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
if (create) {
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto put_out;
}
err = __allocate_data_block(&dn);
if (err)
goto put_out;
allocated = true;
map->m_flags = F2FS_MAP_NEW;
} else {
if (flag != F2FS_GET_BLOCK_FIEMAP ||
dn.data_blkaddr != NEW_ADDR) {
if (flag == F2FS_GET_BLOCK_BMAP)
err = -ENOENT;
goto put_out;
}
/*
* preallocated unwritten block should be mapped
* for fiemap.
*/
if (dn.data_blkaddr == NEW_ADDR)
map->m_flags = F2FS_MAP_UNWRITTEN;
}
}
map->m_flags |= F2FS_MAP_MAPPED;
map->m_pblk = dn.data_blkaddr;
map->m_len = 1;
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
dn.ofs_in_node++;
pgofs++;
get_next:
if (map->m_len >= maxblocks)
goto sync_out;
if (dn.ofs_in_node >= end_offset) {
if (allocated)
sync_inode_page(&dn);
allocated = false;
f2fs_put_dnode(&dn);
if (create) {
f2fs_unlock_op(sbi);
f2fs_balance_fs(sbi, dn.node_changed);
f2fs_lock_op(sbi);
}
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, pgofs, mode);
if (err) {
if (err == -ENOENT)
err = 0;
goto unlock_out;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
}
next_block:
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {
@ -678,44 +668,78 @@ get_next:
err = -EIO; err = -EIO;
goto sync_out; goto sync_out;
} }
err = __allocate_data_block(&dn); if (flag == F2FS_GET_BLOCK_PRE_AIO) {
if (blkaddr == NULL_ADDR)
err = reserve_new_block(&dn);
} else {
err = __allocate_data_block(&dn);
}
if (err) if (err)
goto sync_out; goto sync_out;
allocated = true; allocated = true;
map->m_flags |= F2FS_MAP_NEW; map->m_flags = F2FS_MAP_NEW;
blkaddr = dn.data_blkaddr; blkaddr = dn.data_blkaddr;
} else { } else {
/* if (flag == F2FS_GET_BLOCK_FIEMAP &&
* we only merge preallocated unwritten blocks blkaddr == NULL_ADDR) {
* for fiemap. if (map->m_next_pgofs)
*/ *map->m_next_pgofs = pgofs + 1;
}
if (flag != F2FS_GET_BLOCK_FIEMAP || if (flag != F2FS_GET_BLOCK_FIEMAP ||
blkaddr != NEW_ADDR) blkaddr != NEW_ADDR) {
if (flag == F2FS_GET_BLOCK_BMAP)
err = -ENOENT;
goto sync_out; goto sync_out;
}
} }
} }
/* Give more consecutive addresses for the readahead */ if (map->m_len == 0) {
if ((map->m_pblk != NEW_ADDR && /* preallocated unwritten block should be mapped for fiemap. */
if (blkaddr == NEW_ADDR)
map->m_flags |= F2FS_MAP_UNWRITTEN;
map->m_flags |= F2FS_MAP_MAPPED;
map->m_pblk = blkaddr;
map->m_len = 1;
} else if ((map->m_pblk != NEW_ADDR &&
blkaddr == (map->m_pblk + ofs)) || blkaddr == (map->m_pblk + ofs)) ||
(map->m_pblk == NEW_ADDR && (map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR) ||
blkaddr == NEW_ADDR)) { flag == F2FS_GET_BLOCK_PRE_DIO ||
flag == F2FS_GET_BLOCK_PRE_AIO) {
ofs++; ofs++;
dn.ofs_in_node++;
pgofs++;
map->m_len++; map->m_len++;
goto get_next; } else {
goto sync_out;
}
dn.ofs_in_node++;
pgofs++;
if (map->m_len < maxblocks) {
if (dn.ofs_in_node < end_offset)
goto next_block;
if (allocated)
sync_inode_page(&dn);
f2fs_put_dnode(&dn);
if (create) {
f2fs_unlock_op(sbi);
f2fs_balance_fs(sbi, allocated);
}
allocated = false;
goto next_dnode;
} }
sync_out: sync_out:
if (allocated) if (allocated)
sync_inode_page(&dn); sync_inode_page(&dn);
put_out:
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
unlock_out: unlock_out:
if (create) { if (create) {
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
f2fs_balance_fs(sbi, dn.node_changed); f2fs_balance_fs(sbi, allocated);
} }
out: out:
trace_f2fs_map_blocks(inode, map, err); trace_f2fs_map_blocks(inode, map, err);
@ -723,13 +747,15 @@ out:
} }
static int __get_data_block(struct inode *inode, sector_t iblock, static int __get_data_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh, int create, int flag) struct buffer_head *bh, int create, int flag,
pgoff_t *next_pgofs)
{ {
struct f2fs_map_blocks map; struct f2fs_map_blocks map;
int ret; int ret;
map.m_lblk = iblock; map.m_lblk = iblock;
map.m_len = bh->b_size >> inode->i_blkbits; map.m_len = bh->b_size >> inode->i_blkbits;
map.m_next_pgofs = next_pgofs;
ret = f2fs_map_blocks(inode, &map, create, flag); ret = f2fs_map_blocks(inode, &map, create, flag);
if (!ret) { if (!ret) {
@ -741,16 +767,18 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
} }
static int get_data_block(struct inode *inode, sector_t iblock, static int get_data_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create, int flag) struct buffer_head *bh_result, int create, int flag,
pgoff_t *next_pgofs)
{ {
return __get_data_block(inode, iblock, bh_result, create, flag); return __get_data_block(inode, iblock, bh_result, create,
flag, next_pgofs);
} }
static int get_data_block_dio(struct inode *inode, sector_t iblock, static int get_data_block_dio(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create) struct buffer_head *bh_result, int create)
{ {
return __get_data_block(inode, iblock, bh_result, create, return __get_data_block(inode, iblock, bh_result, create,
F2FS_GET_BLOCK_DIO); F2FS_GET_BLOCK_DIO, NULL);
} }
static int get_data_block_bmap(struct inode *inode, sector_t iblock, static int get_data_block_bmap(struct inode *inode, sector_t iblock,
@ -761,7 +789,7 @@ static int get_data_block_bmap(struct inode *inode, sector_t iblock,
return -EFBIG; return -EFBIG;
return __get_data_block(inode, iblock, bh_result, create, return __get_data_block(inode, iblock, bh_result, create,
F2FS_GET_BLOCK_BMAP); F2FS_GET_BLOCK_BMAP, NULL);
} }
static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
@ -779,6 +807,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
{ {
struct buffer_head map_bh; struct buffer_head map_bh;
sector_t start_blk, last_blk; sector_t start_blk, last_blk;
pgoff_t next_pgofs;
loff_t isize; loff_t isize;
u64 logical = 0, phys = 0, size = 0; u64 logical = 0, phys = 0, size = 0;
u32 flags = 0; u32 flags = 0;
@ -814,14 +843,15 @@ next:
map_bh.b_size = len; map_bh.b_size = len;
ret = get_data_block(inode, start_blk, &map_bh, 0, ret = get_data_block(inode, start_blk, &map_bh, 0,
F2FS_GET_BLOCK_FIEMAP); F2FS_GET_BLOCK_FIEMAP, &next_pgofs);
if (ret) if (ret)
goto out; goto out;
/* HOLE */ /* HOLE */
if (!buffer_mapped(&map_bh)) { if (!buffer_mapped(&map_bh)) {
start_blk = next_pgofs;
/* Go through holes util pass the EOF */ /* Go through holes util pass the EOF */
if (blk_to_logical(inode, start_blk++) < isize) if (blk_to_logical(inode, start_blk) < isize)
goto prep_next; goto prep_next;
/* Found a hole beyond isize means no more extents. /* Found a hole beyond isize means no more extents.
* Note that the premise is that filesystems don't * Note that the premise is that filesystems don't
@ -889,6 +919,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
map.m_lblk = 0; map.m_lblk = 0;
map.m_len = 0; map.m_len = 0;
map.m_flags = 0; map.m_flags = 0;
map.m_next_pgofs = NULL;
for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { for (page_idx = 0; nr_pages; page_idx++, nr_pages--) {
@ -927,7 +958,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
map.m_len = last_block - block_in_file; map.m_len = last_block - block_in_file;
if (f2fs_map_blocks(inode, &map, 0, if (f2fs_map_blocks(inode, &map, 0,
F2FS_GET_BLOCK_READ)) F2FS_GET_BLOCK_READ))
goto set_error_page; goto set_error_page;
} }
got_it: got_it:
@ -956,12 +987,12 @@ submit_and_realloc:
bio = NULL; bio = NULL;
} }
if (bio == NULL) { if (bio == NULL) {
struct f2fs_crypto_ctx *ctx = NULL; struct fscrypt_ctx *ctx = NULL;
if (f2fs_encrypted_inode(inode) && if (f2fs_encrypted_inode(inode) &&
S_ISREG(inode->i_mode)) { S_ISREG(inode->i_mode)) {
ctx = f2fs_get_crypto_ctx(inode); ctx = fscrypt_get_ctx(inode);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
goto set_error_page; goto set_error_page;
@ -974,7 +1005,7 @@ submit_and_realloc:
min_t(int, nr_pages, BIO_MAX_PAGES)); min_t(int, nr_pages, BIO_MAX_PAGES));
if (!bio) { if (!bio) {
if (ctx) if (ctx)
f2fs_release_crypto_ctx(ctx); fscrypt_release_ctx(ctx);
goto set_error_page; goto set_error_page;
} }
bio->bi_bdev = bdev; bio->bi_bdev = bdev;
@ -1052,10 +1083,10 @@ int do_write_data_page(struct f2fs_io_info *fio)
if (err) if (err)
return err; return err;
fio->blk_addr = dn.data_blkaddr; fio->old_blkaddr = dn.data_blkaddr;
/* This page is already truncated */ /* This page is already truncated */
if (fio->blk_addr == NULL_ADDR) { if (fio->old_blkaddr == NULL_ADDR) {
ClearPageUptodate(page); ClearPageUptodate(page);
goto out_writepage; goto out_writepage;
} }
@ -1064,9 +1095,9 @@ int do_write_data_page(struct f2fs_io_info *fio)
/* wait for GCed encrypted page writeback */ /* wait for GCed encrypted page writeback */
f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode), f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode),
fio->blk_addr); fio->old_blkaddr);
fio->encrypted_page = f2fs_encrypt(inode, fio->page); fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page);
if (IS_ERR(fio->encrypted_page)) { if (IS_ERR(fio->encrypted_page)) {
err = PTR_ERR(fio->encrypted_page); err = PTR_ERR(fio->encrypted_page);
goto out_writepage; goto out_writepage;
@ -1079,7 +1110,7 @@ int do_write_data_page(struct f2fs_io_info *fio)
* If current allocation needs SSR, * If current allocation needs SSR,
* it had better in-place writes for updated data. * it had better in-place writes for updated data.
*/ */
if (unlikely(fio->blk_addr != NEW_ADDR && if (unlikely(fio->old_blkaddr != NEW_ADDR &&
!is_cold_data(page) && !is_cold_data(page) &&
!IS_ATOMIC_WRITTEN_PAGE(page) && !IS_ATOMIC_WRITTEN_PAGE(page) &&
need_inplace_update(inode))) { need_inplace_update(inode))) {
@ -1088,8 +1119,6 @@ int do_write_data_page(struct f2fs_io_info *fio)
trace_f2fs_do_write_data_page(page, IPU); trace_f2fs_do_write_data_page(page, IPU);
} else { } else {
write_data_page(&dn, fio); write_data_page(&dn, fio);
set_data_blkaddr(&dn);
f2fs_update_extent_cache(&dn);
trace_f2fs_do_write_data_page(page, OPU); trace_f2fs_do_write_data_page(page, OPU);
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
if (page->index == 0) if (page->index == 0)
@ -1177,12 +1206,18 @@ out:
inode_dec_dirty_pages(inode); inode_dec_dirty_pages(inode);
if (err) if (err)
ClearPageUptodate(page); ClearPageUptodate(page);
unlock_page(page);
f2fs_balance_fs(sbi, need_balance_fs); if (wbc->for_reclaim) {
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) { f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE);
f2fs_submit_merged_bio(sbi, DATA, WRITE);
remove_dirty_inode(inode); remove_dirty_inode(inode);
} }
unlock_page(page);
f2fs_balance_fs(sbi, need_balance_fs);
if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, DATA, WRITE);
return 0; return 0;
redirty_out: redirty_out:
@ -1282,7 +1317,8 @@ continue_unlock:
if (PageWriteback(page)) { if (PageWriteback(page)) {
if (wbc->sync_mode != WB_SYNC_NONE) if (wbc->sync_mode != WB_SYNC_NONE)
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page,
DATA, true);
else else
goto continue_unlock; goto continue_unlock;
} }
@ -1339,8 +1375,6 @@ static int f2fs_write_data_pages(struct address_space *mapping,
int ret; int ret;
long diff; long diff;
trace_f2fs_writepages(mapping->host, wbc, DATA);
/* deal with chardevs and other special file */ /* deal with chardevs and other special file */
if (!mapping->a_ops->writepage) if (!mapping->a_ops->writepage)
return 0; return 0;
@ -1362,14 +1396,16 @@ static int f2fs_write_data_pages(struct address_space *mapping,
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
goto skip_write; goto skip_write;
trace_f2fs_writepages(mapping->host, wbc, DATA);
diff = nr_pages_to_write(sbi, DATA, wbc); diff = nr_pages_to_write(sbi, DATA, wbc);
if (!S_ISDIR(inode->i_mode)) { if (!S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_ALL) {
mutex_lock(&sbi->writepages); mutex_lock(&sbi->writepages);
locked = true; locked = true;
} }
ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
f2fs_submit_merged_bio(sbi, DATA, WRITE); f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
if (locked) if (locked)
mutex_unlock(&sbi->writepages); mutex_unlock(&sbi->writepages);
@ -1380,6 +1416,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
skip_write: skip_write:
wbc->pages_skipped += get_dirty_pages(inode); wbc->pages_skipped += get_dirty_pages(inode);
trace_f2fs_writepages(mapping->host, wbc, DATA);
return 0; return 0;
} }
@ -1406,6 +1443,14 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
struct extent_info ei; struct extent_info ei;
int err = 0; int err = 0;
/*
* we already allocated all the blocks, so we don't need to get
* the block addresses when there is no need to fill the page.
*/
if (!f2fs_has_inline_data(inode) && !f2fs_encrypted_inode(inode) &&
len == PAGE_CACHE_SIZE)
return 0;
if (f2fs_has_inline_data(inode) || if (f2fs_has_inline_data(inode) ||
(pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { (pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
@ -1425,7 +1470,7 @@ restart:
if (pos + len <= MAX_INLINE_DATA) { if (pos + len <= MAX_INLINE_DATA) {
read_inline_data(page, ipage); read_inline_data(page, ipage);
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
sync_inode_page(&dn); set_inline_node(ipage);
} else { } else {
err = f2fs_convert_inline_page(&dn, page); err = f2fs_convert_inline_page(&dn, page);
if (err) if (err)
@ -1439,13 +1484,9 @@ restart:
if (f2fs_lookup_extent_cache(inode, index, &ei)) { if (f2fs_lookup_extent_cache(inode, index, &ei)) {
dn.data_blkaddr = ei.blk + index - ei.fofs; dn.data_blkaddr = ei.blk + index - ei.fofs;
} else { } else {
bool restart = false;
/* hole case */ /* hole case */
err = get_dnode_of_data(&dn, index, LOOKUP_NODE); err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err || (!err && dn.data_blkaddr == NULL_ADDR)) if (err || (!err && dn.data_blkaddr == NULL_ADDR)) {
restart = true;
if (restart) {
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
locked = true; locked = true;
@ -1514,7 +1555,7 @@ repeat:
} }
} }
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, false);
/* wait for GCed encrypted page writeback */ /* wait for GCed encrypted page writeback */
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
@ -1541,7 +1582,8 @@ repeat:
.sbi = sbi, .sbi = sbi,
.type = DATA, .type = DATA,
.rw = READ_SYNC, .rw = READ_SYNC,
.blk_addr = blkaddr, .old_blkaddr = blkaddr,
.new_blkaddr = blkaddr,
.page = page, .page = page,
.encrypted_page = NULL, .encrypted_page = NULL,
}; };
@ -1561,7 +1603,7 @@ repeat:
/* avoid symlink page */ /* avoid symlink page */
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
err = f2fs_decrypt_one(inode, page); err = fscrypt_decrypt_page(page);
if (err) if (err)
goto fail; goto fail;
} }
@ -1592,7 +1634,6 @@ static int f2fs_write_end(struct file *file,
if (pos + copied > i_size_read(inode)) { if (pos + copied > i_size_read(inode)) {
i_size_write(inode, pos + copied); i_size_write(inode, pos + copied);
mark_inode_dirty(inode); mark_inode_dirty(inode);
update_inode_page(inode);
} }
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
@ -1617,34 +1658,21 @@ static int check_direct_IO(struct inode *inode, struct iov_iter *iter,
static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
loff_t offset) loff_t offset)
{ {
struct file *file = iocb->ki_filp; struct address_space *mapping = iocb->ki_filp->f_mapping;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
size_t count = iov_iter_count(iter); size_t count = iov_iter_count(iter);
int err; int err;
/* we don't need to use inline_data strictly */ err = check_direct_IO(inode, iter, offset);
err = f2fs_convert_inline_inode(inode);
if (err) if (err)
return err; return err;
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
return 0; return 0;
err = check_direct_IO(inode, iter, offset);
if (err)
return err;
trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
if (iov_iter_rw(iter) == WRITE) {
err = __allocate_data_blocks(inode, offset, count);
if (err)
goto out;
}
err = blockdev_direct_IO(iocb, inode, iter, offset, get_data_block_dio); err = blockdev_direct_IO(iocb, inode, iter, offset, get_data_block_dio);
out:
if (err < 0 && iov_iter_rw(iter) == WRITE) if (err < 0 && iov_iter_rw(iter) == WRITE)
f2fs_write_failed(mapping, offset + count); f2fs_write_failed(mapping, offset + count);

View File

@ -77,7 +77,7 @@ static unsigned long dir_block_index(unsigned int level,
} }
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
struct f2fs_filename *fname, struct fscrypt_name *fname,
f2fs_hash_t namehash, f2fs_hash_t namehash,
int *max_slots, int *max_slots,
struct page **res_page) struct page **res_page)
@ -103,15 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
return de; return de;
} }
struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname, struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
f2fs_hash_t namehash, int *max_slots, f2fs_hash_t namehash, int *max_slots,
struct f2fs_dentry_ptr *d) struct f2fs_dentry_ptr *d)
{ {
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
unsigned long bit_pos = 0; unsigned long bit_pos = 0;
int max_len = 0; int max_len = 0;
struct f2fs_str de_name = FSTR_INIT(NULL, 0); struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
struct f2fs_str *name = &fname->disk_name; struct fscrypt_str *name = &fname->disk_name;
if (max_slots) if (max_slots)
*max_slots = 0; *max_slots = 0;
@ -157,7 +157,7 @@ found:
static struct f2fs_dir_entry *find_in_level(struct inode *dir, static struct f2fs_dir_entry *find_in_level(struct inode *dir,
unsigned int level, unsigned int level,
struct f2fs_filename *fname, struct fscrypt_name *fname,
struct page **res_page) struct page **res_page)
{ {
struct qstr name = FSTR_TO_QSTR(&fname->disk_name); struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
@ -218,12 +218,12 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
struct f2fs_dir_entry *de = NULL; struct f2fs_dir_entry *de = NULL;
unsigned int max_depth; unsigned int max_depth;
unsigned int level; unsigned int level;
struct f2fs_filename fname; struct fscrypt_name fname;
int err; int err;
*res_page = NULL; *res_page = NULL;
err = f2fs_fname_setup_filename(dir, child, 1, &fname); err = fscrypt_setup_filename(dir, child, 1, &fname);
if (err) if (err)
return NULL; return NULL;
@ -251,7 +251,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
break; break;
} }
out: out:
f2fs_fname_free_filename(&fname); fscrypt_free_filename(&fname);
return de; return de;
} }
@ -296,7 +296,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
{ {
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA; enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
lock_page(page); lock_page(page);
f2fs_wait_on_page_writeback(page, type); f2fs_wait_on_page_writeback(page, type, true);
de->ino = cpu_to_le32(inode->i_ino); de->ino = cpu_to_le32(inode->i_ino);
set_de_type(de, inode->i_mode); set_de_type(de, inode->i_mode);
f2fs_dentry_kunmap(dir, page); f2fs_dentry_kunmap(dir, page);
@ -311,7 +311,7 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
{ {
struct f2fs_inode *ri; struct f2fs_inode *ri;
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
/* copy name info. to this inode page */ /* copy name info. to this inode page */
ri = F2FS_INODE(ipage); ri = F2FS_INODE(ipage);
@ -341,24 +341,14 @@ int update_dent_inode(struct inode *inode, struct inode *to,
void do_make_empty_dir(struct inode *inode, struct inode *parent, void do_make_empty_dir(struct inode *inode, struct inode *parent,
struct f2fs_dentry_ptr *d) struct f2fs_dentry_ptr *d)
{ {
struct f2fs_dir_entry *de; struct qstr dot = QSTR_INIT(".", 1);
struct qstr dotdot = QSTR_INIT("..", 2);
de = &d->dentry[0]; /* update dirent of "." */
de->name_len = cpu_to_le16(1); f2fs_update_dentry(inode->i_ino, inode->i_mode, d, &dot, 0, 0);
de->hash_code = 0;
de->ino = cpu_to_le32(inode->i_ino);
memcpy(d->filename[0], ".", 1);
set_de_type(de, inode->i_mode);
de = &d->dentry[1]; /* update dirent of ".." */
de->hash_code = 0; f2fs_update_dentry(parent->i_ino, parent->i_mode, d, &dotdot, 0, 1);
de->name_len = cpu_to_le16(2);
de->ino = cpu_to_le32(parent->i_ino);
memcpy(d->filename[1], "..", 2);
set_de_type(de, parent->i_mode);
test_and_set_bit_le(0, (void *)d->bitmap);
test_and_set_bit_le(1, (void *)d->bitmap);
} }
static int make_empty_dir(struct inode *inode, static int make_empty_dir(struct inode *inode,
@ -413,7 +403,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
goto put_error; goto put_error;
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) { if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
err = f2fs_inherit_context(dir, inode, page); err = fscrypt_inherit_context(dir, inode, page, false);
if (err) if (err)
goto put_error; goto put_error;
} }
@ -511,8 +501,12 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d,
memcpy(d->filename[bit_pos], name->name, name->len); memcpy(d->filename[bit_pos], name->name, name->len);
de->ino = cpu_to_le32(ino); de->ino = cpu_to_le32(ino);
set_de_type(de, mode); set_de_type(de, mode);
for (i = 0; i < slots; i++) for (i = 0; i < slots; i++) {
test_and_set_bit_le(bit_pos + i, (void *)d->bitmap); test_and_set_bit_le(bit_pos + i, (void *)d->bitmap);
/* avoid wrong garbage data for readdir */
if (i)
(de + i)->name_len = 0;
}
} }
/* /*
@ -532,11 +526,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_block *dentry_blk = NULL;
struct f2fs_dentry_ptr d; struct f2fs_dentry_ptr d;
struct page *page = NULL; struct page *page = NULL;
struct f2fs_filename fname; struct fscrypt_name fname;
struct qstr new_name; struct qstr new_name;
int slots, err; int slots, err;
err = f2fs_fname_setup_filename(dir, name, 0, &fname); err = fscrypt_setup_filename(dir, name, 0, &fname);
if (err) if (err)
return err; return err;
@ -598,7 +592,7 @@ start:
++level; ++level;
goto start; goto start;
add_dentry: add_dentry:
f2fs_wait_on_page_writeback(dentry_page, DATA); f2fs_wait_on_page_writeback(dentry_page, DATA, true);
if (inode) { if (inode) {
down_write(&F2FS_I(inode)->i_sem); down_write(&F2FS_I(inode)->i_sem);
@ -635,7 +629,7 @@ fail:
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
out: out:
f2fs_fname_free_filename(&fname); fscrypt_free_filename(&fname);
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
return err; return err;
} }
@ -709,7 +703,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
return f2fs_delete_inline_entry(dentry, page, dir, inode); return f2fs_delete_inline_entry(dentry, page, dir, inode);
lock_page(page); lock_page(page);
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, true);
dentry_blk = page_address(page); dentry_blk = page_address(page);
bit_pos = dentry - dentry_blk->dentry; bit_pos = dentry - dentry_blk->dentry;
@ -777,12 +771,12 @@ bool f2fs_empty_dir(struct inode *dir)
} }
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
unsigned int start_pos, struct f2fs_str *fstr) unsigned int start_pos, struct fscrypt_str *fstr)
{ {
unsigned char d_type = DT_UNKNOWN; unsigned char d_type = DT_UNKNOWN;
unsigned int bit_pos; unsigned int bit_pos;
struct f2fs_dir_entry *de = NULL; struct f2fs_dir_entry *de = NULL;
struct f2fs_str de_name = FSTR_INIT(NULL, 0); struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
bit_pos = ((unsigned long)ctx->pos % d->max); bit_pos = ((unsigned long)ctx->pos % d->max);
@ -792,6 +786,12 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
break; break;
de = &d->dentry[bit_pos]; de = &d->dentry[bit_pos];
if (de->name_len == 0) {
bit_pos++;
ctx->pos = start_pos + bit_pos;
continue;
}
if (de->file_type < F2FS_FT_MAX) if (de->file_type < F2FS_FT_MAX)
d_type = f2fs_filetype_table[de->file_type]; d_type = f2fs_filetype_table[de->file_type];
else else
@ -810,8 +810,9 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
memcpy(de_name.name, d->filename[bit_pos], de_name.len); memcpy(de_name.name, d->filename[bit_pos], de_name.len);
ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code, ret = fscrypt_fname_disk_to_usr(d->inode,
&de_name, fstr); (u32)de->hash_code, 0,
&de_name, fstr);
kfree(de_name.name); kfree(de_name.name);
if (ret < 0) if (ret < 0)
return true; return true;
@ -839,16 +840,15 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
struct file_ra_state *ra = &file->f_ra; struct file_ra_state *ra = &file->f_ra;
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
struct f2fs_dentry_ptr d; struct f2fs_dentry_ptr d;
struct f2fs_str fstr = FSTR_INIT(NULL, 0); struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
int err = 0; int err = 0;
if (f2fs_encrypted_inode(inode)) { if (f2fs_encrypted_inode(inode)) {
err = f2fs_get_encryption_info(inode); err = fscrypt_get_encryption_info(inode);
if (err) if (err && err != -ENOKEY)
return err; return err;
err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN, err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr);
&fstr);
if (err < 0) if (err < 0)
return err; return err;
} }
@ -888,15 +888,23 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
} }
out: out:
f2fs_fname_crypto_free_buffer(&fstr); fscrypt_fname_free_buffer(&fstr);
return err; return err;
} }
static int f2fs_dir_open(struct inode *inode, struct file *filp)
{
if (f2fs_encrypted_inode(inode))
return fscrypt_get_encryption_info(inode) ? -EACCES : 0;
return 0;
}
const struct file_operations f2fs_dir_operations = { const struct file_operations f2fs_dir_operations = {
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.read = generic_read_dir, .read = generic_read_dir,
.iterate = f2fs_readdir, .iterate = f2fs_readdir,
.fsync = f2fs_sync_file, .fsync = f2fs_sync_file,
.open = f2fs_dir_open,
.unlocked_ioctl = f2fs_ioctl, .unlocked_ioctl = f2fs_ioctl,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compat_ioctl = f2fs_compat_ioctl, .compat_ioctl = f2fs_compat_ioctl,

View File

@ -33,6 +33,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
en->ei = *ei; en->ei = *ei;
INIT_LIST_HEAD(&en->list); INIT_LIST_HEAD(&en->list);
en->et = et;
rb_link_node(&en->rb_node, parent, p); rb_link_node(&en->rb_node, parent, p);
rb_insert_color(&en->rb_node, &et->root); rb_insert_color(&en->rb_node, &et->root);
@ -50,6 +51,24 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi,
if (et->cached_en == en) if (et->cached_en == en)
et->cached_en = NULL; et->cached_en = NULL;
kmem_cache_free(extent_node_slab, en);
}
/*
* Flow to release an extent_node:
* 1. list_del_init
* 2. __detach_extent_node
* 3. kmem_cache_free.
*/
static void __release_extent_node(struct f2fs_sb_info *sbi,
struct extent_tree *et, struct extent_node *en)
{
spin_lock(&sbi->extent_lock);
f2fs_bug_on(sbi, list_empty(&en->list));
list_del_init(&en->list);
spin_unlock(&sbi->extent_lock);
__detach_extent_node(sbi, et, en);
} }
static struct extent_tree *__grab_extent_tree(struct inode *inode) static struct extent_tree *__grab_extent_tree(struct inode *inode)
@ -129,7 +148,7 @@ static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi,
} }
static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
struct extent_tree *et, bool free_all) struct extent_tree *et)
{ {
struct rb_node *node, *next; struct rb_node *node, *next;
struct extent_node *en; struct extent_node *en;
@ -139,18 +158,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
while (node) { while (node) {
next = rb_next(node); next = rb_next(node);
en = rb_entry(node, struct extent_node, rb_node); en = rb_entry(node, struct extent_node, rb_node);
__release_extent_node(sbi, et, en);
if (free_all) {
spin_lock(&sbi->extent_lock);
if (!list_empty(&en->list))
list_del_init(&en->list);
spin_unlock(&sbi->extent_lock);
}
if (free_all || list_empty(&en->list)) {
__detach_extent_node(sbi, et, en);
kmem_cache_free(extent_node_slab, en);
}
node = next; node = next;
} }
@ -232,9 +240,10 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
if (en) { if (en) {
*ei = en->ei; *ei = en->ei;
spin_lock(&sbi->extent_lock); spin_lock(&sbi->extent_lock);
if (!list_empty(&en->list)) if (!list_empty(&en->list)) {
list_move_tail(&en->list, &sbi->extent_list); list_move_tail(&en->list, &sbi->extent_list);
et->cached_en = en; et->cached_en = en;
}
spin_unlock(&sbi->extent_lock); spin_unlock(&sbi->extent_lock);
ret = true; ret = true;
} }
@ -329,7 +338,6 @@ lookup_neighbors:
static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
struct extent_tree *et, struct extent_info *ei, struct extent_tree *et, struct extent_info *ei,
struct extent_node **den,
struct extent_node *prev_ex, struct extent_node *prev_ex,
struct extent_node *next_ex) struct extent_node *next_ex)
{ {
@ -342,20 +350,25 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
} }
if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) { if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) {
if (en) { if (en)
__detach_extent_node(sbi, et, prev_ex); __release_extent_node(sbi, et, prev_ex);
*den = prev_ex;
}
next_ex->ei.fofs = ei->fofs; next_ex->ei.fofs = ei->fofs;
next_ex->ei.blk = ei->blk; next_ex->ei.blk = ei->blk;
next_ex->ei.len += ei->len; next_ex->ei.len += ei->len;
en = next_ex; en = next_ex;
} }
if (en) { if (!en)
__try_update_largest_extent(et, en); return NULL;
__try_update_largest_extent(et, en);
spin_lock(&sbi->extent_lock);
if (!list_empty(&en->list)) {
list_move_tail(&en->list, &sbi->extent_list);
et->cached_en = en; et->cached_en = en;
} }
spin_unlock(&sbi->extent_lock);
return en; return en;
} }
@ -391,7 +404,12 @@ do_insert:
return NULL; return NULL;
__try_update_largest_extent(et, en); __try_update_largest_extent(et, en);
/* update in global extent list */
spin_lock(&sbi->extent_lock);
list_add_tail(&en->list, &sbi->extent_list);
et->cached_en = en; et->cached_en = en;
spin_unlock(&sbi->extent_lock);
return en; return en;
} }
@ -479,7 +497,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
if (parts) if (parts)
__try_update_largest_extent(et, en); __try_update_largest_extent(et, en);
else else
__detach_extent_node(sbi, et, en); __release_extent_node(sbi, et, en);
/* /*
* if original extent is split into zero or two parts, extent * if original extent is split into zero or two parts, extent
@ -490,31 +508,15 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
insert_p = NULL; insert_p = NULL;
insert_parent = NULL; insert_parent = NULL;
} }
/* update in global extent list */
spin_lock(&sbi->extent_lock);
if (!parts && !list_empty(&en->list))
list_del(&en->list);
if (en1)
list_add_tail(&en1->list, &sbi->extent_list);
spin_unlock(&sbi->extent_lock);
/* release extent node */
if (!parts)
kmem_cache_free(extent_node_slab, en);
en = next_en; en = next_en;
} }
/* 3. update extent in extent cache */ /* 3. update extent in extent cache */
if (blkaddr) { if (blkaddr) {
struct extent_node *den = NULL;
set_extent_info(&ei, fofs, blkaddr, len); set_extent_info(&ei, fofs, blkaddr, len);
en1 = __try_merge_extent_node(sbi, et, &ei, &den, if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en))
prev_en, next_en); __insert_extent_tree(sbi, et, &ei,
if (!en1)
en1 = __insert_extent_tree(sbi, et, &ei,
insert_p, insert_parent); insert_p, insert_parent);
/* give up extent_cache, if split and small updates happen */ /* give up extent_cache, if split and small updates happen */
@ -524,24 +526,10 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
et->largest.len = 0; et->largest.len = 0;
set_inode_flag(F2FS_I(inode), FI_NO_EXTENT); set_inode_flag(F2FS_I(inode), FI_NO_EXTENT);
} }
spin_lock(&sbi->extent_lock);
if (en1) {
if (list_empty(&en1->list))
list_add_tail(&en1->list, &sbi->extent_list);
else
list_move_tail(&en1->list, &sbi->extent_list);
}
if (den && !list_empty(&den->list))
list_del(&den->list);
spin_unlock(&sbi->extent_lock);
if (den)
kmem_cache_free(extent_node_slab, den);
} }
if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
__free_extent_tree(sbi, et, true); __free_extent_tree(sbi, et);
write_unlock(&et->lock); write_unlock(&et->lock);
@ -550,14 +538,10 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
{ {
struct extent_tree *treevec[EXT_TREE_VEC_SIZE];
struct extent_tree *et, *next; struct extent_tree *et, *next;
struct extent_node *en, *tmp; struct extent_node *en;
unsigned long ino = F2FS_ROOT_INO(sbi);
unsigned int found;
unsigned int node_cnt = 0, tree_cnt = 0; unsigned int node_cnt = 0, tree_cnt = 0;
int remained; int remained;
bool do_free = false;
if (!test_opt(sbi, EXTENT_CACHE)) if (!test_opt(sbi, EXTENT_CACHE))
return 0; return 0;
@ -572,10 +556,10 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
if (atomic_read(&et->node_cnt)) { if (atomic_read(&et->node_cnt)) {
write_lock(&et->lock); write_lock(&et->lock);
node_cnt += __free_extent_tree(sbi, et, true); node_cnt += __free_extent_tree(sbi, et);
write_unlock(&et->lock); write_unlock(&et->lock);
} }
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
list_del_init(&et->list); list_del_init(&et->list);
radix_tree_delete(&sbi->extent_tree_root, et->ino); radix_tree_delete(&sbi->extent_tree_root, et->ino);
kmem_cache_free(extent_tree_slab, et); kmem_cache_free(extent_tree_slab, et);
@ -585,6 +569,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
if (node_cnt + tree_cnt >= nr_shrink) if (node_cnt + tree_cnt >= nr_shrink)
goto unlock_out; goto unlock_out;
cond_resched();
} }
up_write(&sbi->extent_tree_lock); up_write(&sbi->extent_tree_lock);
@ -596,42 +581,29 @@ free_node:
remained = nr_shrink - (node_cnt + tree_cnt); remained = nr_shrink - (node_cnt + tree_cnt);
spin_lock(&sbi->extent_lock); spin_lock(&sbi->extent_lock);
list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) { for (; remained > 0; remained--) {
if (!remained--) if (list_empty(&sbi->extent_list))
break; break;
en = list_first_entry(&sbi->extent_list,
struct extent_node, list);
et = en->et;
if (!write_trylock(&et->lock)) {
/* refresh this extent node's position in extent list */
list_move_tail(&en->list, &sbi->extent_list);
continue;
}
list_del_init(&en->list); list_del_init(&en->list);
do_free = true; spin_unlock(&sbi->extent_lock);
__detach_extent_node(sbi, et, en);
write_unlock(&et->lock);
node_cnt++;
spin_lock(&sbi->extent_lock);
} }
spin_unlock(&sbi->extent_lock); spin_unlock(&sbi->extent_lock);
if (do_free == false)
goto unlock_out;
/*
* reset ino for searching victims from beginning of global extent tree.
*/
ino = F2FS_ROOT_INO(sbi);
while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root,
(void **)treevec, ino, EXT_TREE_VEC_SIZE))) {
unsigned i;
ino = treevec[found - 1]->ino + 1;
for (i = 0; i < found; i++) {
struct extent_tree *et = treevec[i];
if (!atomic_read(&et->node_cnt))
continue;
if (write_trylock(&et->lock)) {
node_cnt += __free_extent_tree(sbi, et, false);
write_unlock(&et->lock);
}
if (node_cnt + tree_cnt >= nr_shrink)
goto unlock_out;
}
}
unlock_out: unlock_out:
up_write(&sbi->extent_tree_lock); up_write(&sbi->extent_tree_lock);
out: out:
@ -650,7 +622,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode)
return 0; return 0;
write_lock(&et->lock); write_lock(&et->lock);
node_cnt = __free_extent_tree(sbi, et, true); node_cnt = __free_extent_tree(sbi, et);
write_unlock(&et->lock); write_unlock(&et->lock);
return node_cnt; return node_cnt;
@ -701,19 +673,21 @@ bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
void f2fs_update_extent_cache(struct dnode_of_data *dn) void f2fs_update_extent_cache(struct dnode_of_data *dn)
{ {
struct f2fs_inode_info *fi = F2FS_I(dn->inode);
pgoff_t fofs; pgoff_t fofs;
block_t blkaddr;
if (!f2fs_may_extent_tree(dn->inode)) if (!f2fs_may_extent_tree(dn->inode))
return; return;
f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR); if (dn->data_blkaddr == NEW_ADDR)
blkaddr = NULL_ADDR;
else
blkaddr = dn->data_blkaddr;
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) +
dn->ofs_in_node;
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1))
dn->ofs_in_node;
if (f2fs_update_extent_tree_range(dn->inode, fofs, dn->data_blkaddr, 1))
sync_inode_page(dn); sync_inode_page(dn);
} }

View File

@ -22,10 +22,11 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/fscrypto.h>
#include <crypto/hash.h>
#ifdef CONFIG_F2FS_CHECK_FS #ifdef CONFIG_F2FS_CHECK_FS
#define f2fs_bug_on(sbi, condition) BUG_ON(condition) #define f2fs_bug_on(sbi, condition) BUG_ON(condition)
#define f2fs_down_write(x, y) down_write_nest_lock(x, y)
#else #else
#define f2fs_bug_on(sbi, condition) \ #define f2fs_bug_on(sbi, condition) \
do { \ do { \
@ -34,7 +35,6 @@
set_sbi_flag(sbi, SBI_NEED_FSCK); \ set_sbi_flag(sbi, SBI_NEED_FSCK); \
} \ } \
} while (0) } while (0)
#define f2fs_down_write(x, y) down_write(x)
#endif #endif
/* /*
@ -84,27 +84,6 @@ struct f2fs_mount_info {
#define F2FS_CLEAR_FEATURE(sb, mask) \ #define F2FS_CLEAR_FEATURE(sb, mask) \
F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask) F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask)
#define CRCPOLY_LE 0xedb88320
static inline __u32 f2fs_crc32(void *buf, size_t len)
{
unsigned char *p = (unsigned char *)buf;
__u32 crc = F2FS_SUPER_MAGIC;
int i;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
}
return crc;
}
static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size)
{
return f2fs_crc32(buf, buf_size) == blk_crc;
}
/* /*
* For checkpoint manager * For checkpoint manager
*/ */
@ -183,37 +162,37 @@ struct fsync_inode_entry {
block_t last_inode; /* block address locating the last inode */ block_t last_inode; /* block address locating the last inode */
}; };
#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats)) #define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats))
#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits)) #define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits))
#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne) #define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne)
#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid) #define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid)
#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se) #define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se)
#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno) #define segno_in_journal(jnl, i) (jnl->sit_j.entries[i].segno)
#define MAX_NAT_JENTRIES(sum) (NAT_JOURNAL_ENTRIES - nats_in_cursum(sum)) #define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl))
#define MAX_SIT_JENTRIES(sum) (SIT_JOURNAL_ENTRIES - sits_in_cursum(sum)) #define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl))
static inline int update_nats_in_cursum(struct f2fs_summary_block *rs, int i) static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i)
{ {
int before = nats_in_cursum(rs); int before = nats_in_cursum(journal);
rs->n_nats = cpu_to_le16(before + i); journal->n_nats = cpu_to_le16(before + i);
return before; return before;
} }
static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i) static inline int update_sits_in_cursum(struct f2fs_journal *journal, int i)
{ {
int before = sits_in_cursum(rs); int before = sits_in_cursum(journal);
rs->n_sits = cpu_to_le16(before + i); journal->n_sits = cpu_to_le16(before + i);
return before; return before;
} }
static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, static inline bool __has_cursum_space(struct f2fs_journal *journal,
int type) int size, int type)
{ {
if (type == NAT_JOURNAL) if (type == NAT_JOURNAL)
return size <= MAX_NAT_JENTRIES(sum); return size <= MAX_NAT_JENTRIES(journal);
return size <= MAX_SIT_JENTRIES(sum); return size <= MAX_SIT_JENTRIES(journal);
} }
/* /*
@ -233,12 +212,9 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
#define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) #define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7)
#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8) #define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8)
#define F2FS_IOC_SET_ENCRYPTION_POLICY \ #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
_IOR('f', 19, struct f2fs_encryption_policy) #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
#define F2FS_IOC_GET_ENCRYPTION_PWSALT \ #define F2FS_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT
_IOW('f', 20, __u8[16])
#define F2FS_IOC_GET_ENCRYPTION_POLICY \
_IOW('f', 21, struct f2fs_encryption_policy)
/* /*
* should be same as XFS_IOC_GOINGDOWN. * should be same as XFS_IOC_GOINGDOWN.
@ -268,25 +244,6 @@ struct f2fs_defragment {
* For INODE and NODE manager * For INODE and NODE manager
*/ */
/* for directory operations */ /* for directory operations */
struct f2fs_str {
unsigned char *name;
u32 len;
};
struct f2fs_filename {
const struct qstr *usr_fname;
struct f2fs_str disk_name;
f2fs_hash_t hash;
#ifdef CONFIG_F2FS_FS_ENCRYPTION
struct f2fs_str crypto_buf;
#endif
};
#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)
struct f2fs_dentry_ptr { struct f2fs_dentry_ptr {
struct inode *inode; struct inode *inode;
const void *bitmap; const void *bitmap;
@ -354,6 +311,7 @@ struct extent_node {
struct rb_node rb_node; /* rb node located in rb-tree */ struct rb_node rb_node; /* rb node located in rb-tree */
struct list_head list; /* node in global extent list of sbi */ struct list_head list; /* node in global extent list of sbi */
struct extent_info ei; /* extent info */ struct extent_info ei; /* extent info */
struct extent_tree *et; /* extent tree pointer */
}; };
struct extent_tree { struct extent_tree {
@ -382,6 +340,7 @@ struct f2fs_map_blocks {
block_t m_lblk; block_t m_lblk;
unsigned int m_len; unsigned int m_len;
unsigned int m_flags; unsigned int m_flags;
pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */
}; };
/* for flag in get_data_block */ /* for flag in get_data_block */
@ -389,6 +348,8 @@ struct f2fs_map_blocks {
#define F2FS_GET_BLOCK_DIO 1 #define F2FS_GET_BLOCK_DIO 1
#define F2FS_GET_BLOCK_FIEMAP 2 #define F2FS_GET_BLOCK_FIEMAP 2
#define F2FS_GET_BLOCK_BMAP 3 #define F2FS_GET_BLOCK_BMAP 3
#define F2FS_GET_BLOCK_PRE_DIO 4
#define F2FS_GET_BLOCK_PRE_AIO 5
/* /*
* i_advise uses FADVISE_XXX_BIT. We can add additional hints later. * i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
@ -410,15 +371,6 @@ struct f2fs_map_blocks {
#define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT) #define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT)
#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT) #define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT)
/* Encryption algorithms */
#define F2FS_ENCRYPTION_MODE_INVALID 0
#define F2FS_ENCRYPTION_MODE_AES_256_XTS 1
#define F2FS_ENCRYPTION_MODE_AES_256_GCM 2
#define F2FS_ENCRYPTION_MODE_AES_256_CBC 3
#define F2FS_ENCRYPTION_MODE_AES_256_CTS 4
#include "f2fs_crypto.h"
#define DEF_DIR_LEVEL 0 #define DEF_DIR_LEVEL 0
struct f2fs_inode_info { struct f2fs_inode_info {
@ -442,13 +394,7 @@ struct f2fs_inode_info {
struct list_head dirty_list; /* linked in global dirty list */ struct list_head dirty_list; /* linked in global dirty list */
struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */
struct mutex inmem_lock; /* lock for inmemory pages */ struct mutex inmem_lock; /* lock for inmemory pages */
struct extent_tree *extent_tree; /* cached extent_tree entry */ struct extent_tree *extent_tree; /* cached extent_tree entry */
#ifdef CONFIG_F2FS_FS_ENCRYPTION
/* Encryption params */
struct f2fs_crypt_info *i_crypt_info;
#endif
}; };
static inline void get_extent_info(struct extent_info *ext, static inline void get_extent_info(struct extent_info *ext,
@ -515,6 +461,7 @@ struct f2fs_nm_info {
nid_t next_scan_nid; /* the next nid to be scanned */ nid_t next_scan_nid; /* the next nid to be scanned */
unsigned int ram_thresh; /* control the memory footprint */ unsigned int ram_thresh; /* control the memory footprint */
unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */ unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */
unsigned int dirty_nats_ratio; /* control dirty nats ratio threshold */
/* NAT cache management */ /* NAT cache management */
struct radix_tree_root nat_root;/* root of the nat entry cache */ struct radix_tree_root nat_root;/* root of the nat entry cache */
@ -549,6 +496,8 @@ struct dnode_of_data {
unsigned int ofs_in_node; /* data offset in the node page */ unsigned int ofs_in_node; /* data offset in the node page */
bool inode_page_locked; /* inode page is locked or not */ bool inode_page_locked; /* inode page is locked or not */
bool node_changed; /* is node block changed */ bool node_changed; /* is node block changed */
char cur_level; /* level of hole node page */
char max_level; /* level of current page located */
block_t data_blkaddr; /* block address of the node block */ block_t data_blkaddr; /* block address of the node block */
}; };
@ -679,6 +628,7 @@ enum page_type {
META_FLUSH, META_FLUSH,
INMEM, /* the below types are used by tracepoints only. */ INMEM, /* the below types are used by tracepoints only. */
INMEM_DROP, INMEM_DROP,
INMEM_REVOKE,
IPU, IPU,
OPU, OPU,
}; };
@ -687,7 +637,8 @@ struct f2fs_io_info {
struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */
enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */
int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */
block_t blk_addr; /* block address to be written */ block_t new_blkaddr; /* new block address to be written */
block_t old_blkaddr; /* old block address before Cow */
struct page *page; /* page to be written */ struct page *page; /* page to be written */
struct page *encrypted_page; /* encrypted page */ struct page *encrypted_page; /* encrypted page */
}; };
@ -844,8 +795,22 @@ struct f2fs_sb_info {
struct list_head s_list; struct list_head s_list;
struct mutex umount_mutex; struct mutex umount_mutex;
unsigned int shrinker_run_no; unsigned int shrinker_run_no;
/* For write statistics */
u64 sectors_written_start;
u64 kbytes_written;
/* Reference to checksum algorithm driver via cryptoapi */
struct crypto_shash *s_chksum_driver;
}; };
/* For write statistics. Suppose sector size is 512 bytes,
* and the return value is in kbytes. s is of struct f2fs_sb_info.
*/
#define BD_PART_WRITTEN(s) \
(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[1]) - \
s->sectors_written_start) >> 1)
static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
{ {
sbi->last_time[type] = jiffies; sbi->last_time[type] = jiffies;
@ -874,6 +839,29 @@ static inline bool is_idle(struct f2fs_sb_info *sbi)
/* /*
* Inline functions * Inline functions
*/ */
static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address,
unsigned int length)
{
SHASH_DESC_ON_STACK(shash, sbi->s_chksum_driver);
u32 *ctx = (u32 *)shash_desc_ctx(shash);
int err;
shash->tfm = sbi->s_chksum_driver;
shash->flags = 0;
*ctx = F2FS_SUPER_MAGIC;
err = crypto_shash_update(shash, address, length);
BUG_ON(err);
return *ctx;
}
static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc,
void *buf, size_t buf_size)
{
return f2fs_crc32(sbi, buf, buf_size) == blk_crc;
}
static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) static inline struct f2fs_inode_info *F2FS_I(struct inode *inode)
{ {
return container_of(inode, struct f2fs_inode_info, vfs_inode); return container_of(inode, struct f2fs_inode_info, vfs_inode);
@ -1006,7 +994,7 @@ static inline void f2fs_unlock_op(struct f2fs_sb_info *sbi)
static inline void f2fs_lock_all(struct f2fs_sb_info *sbi) static inline void f2fs_lock_all(struct f2fs_sb_info *sbi)
{ {
f2fs_down_write(&sbi->cp_rwsem, &sbi->cp_mutex); down_write(&sbi->cp_rwsem);
} }
static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi)
@ -1525,9 +1513,9 @@ static inline int f2fs_has_inline_xattr(struct inode *inode)
return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR); return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR);
} }
static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi) static inline unsigned int addrs_per_inode(struct inode *inode)
{ {
if (f2fs_has_inline_xattr(&fi->vfs_inode)) if (f2fs_has_inline_xattr(inode))
return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS; return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS;
return DEF_ADDRS_PER_INODE; return DEF_ADDRS_PER_INODE;
} }
@ -1681,10 +1669,10 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags)
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) (F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
/* get offset of first page in next direct node */ /* get offset of first page in next direct node */
#define PGOFS_OF_NEXT_DNODE(pgofs, fi) \ #define PGOFS_OF_NEXT_DNODE(pgofs, inode) \
((pgofs < ADDRS_PER_INODE(fi)) ? ADDRS_PER_INODE(fi) : \ ((pgofs < ADDRS_PER_INODE(inode)) ? ADDRS_PER_INODE(inode) : \
(pgofs - ADDRS_PER_INODE(fi) + ADDRS_PER_BLOCK) / \ (pgofs - ADDRS_PER_INODE(inode) + ADDRS_PER_BLOCK) / \
ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi)) ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode))
/* /*
* file.c * file.c
@ -1723,10 +1711,10 @@ struct dentry *f2fs_get_parent(struct dentry *child);
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
void set_de_type(struct f2fs_dir_entry *, umode_t); void set_de_type(struct f2fs_dir_entry *, umode_t);
struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *, struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *,
f2fs_hash_t, int *, struct f2fs_dentry_ptr *); f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
unsigned int, struct f2fs_str *); unsigned int, struct fscrypt_str *);
void do_make_empty_dir(struct inode *, struct inode *, void do_make_empty_dir(struct inode *, struct inode *,
struct f2fs_dentry_ptr *); struct f2fs_dentry_ptr *);
struct page *init_inode_metadata(struct inode *, struct inode *, struct page *init_inode_metadata(struct inode *, struct inode *,
@ -1763,6 +1751,7 @@ int f2fs_commit_super(struct f2fs_sb_info *, bool);
int f2fs_sync_fs(struct super_block *, int); int f2fs_sync_fs(struct super_block *, int);
extern __printf(3, 4) extern __printf(3, 4)
void f2fs_msg(struct super_block *, const char *, const char *, ...); void f2fs_msg(struct super_block *, const char *, const char *, ...);
int sanity_check_ckpt(struct f2fs_sb_info *sbi);
/* /*
* hash.c * hash.c
@ -1780,6 +1769,7 @@ int need_dentry_mark(struct f2fs_sb_info *, nid_t);
bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); bool is_checkpointed_node(struct f2fs_sb_info *, nid_t);
bool need_inode_block_update(struct f2fs_sb_info *, nid_t); bool need_inode_block_update(struct f2fs_sb_info *, nid_t);
void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
pgoff_t get_next_page_offset(struct dnode_of_data *, pgoff_t);
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
int truncate_inode_blocks(struct inode *, pgoff_t); int truncate_inode_blocks(struct inode *, pgoff_t);
int truncate_xattr_node(struct inode *, struct page *); int truncate_xattr_node(struct inode *, struct page *);
@ -1811,7 +1801,8 @@ void destroy_node_manager_caches(void);
* segment.c * segment.c
*/ */
void register_inmem_page(struct inode *, struct page *); void register_inmem_page(struct inode *, struct page *);
int commit_inmem_pages(struct inode *, bool); void drop_inmem_pages(struct inode *);
int commit_inmem_pages(struct inode *);
void f2fs_balance_fs(struct f2fs_sb_info *, bool); void f2fs_balance_fs(struct f2fs_sb_info *, bool);
void f2fs_balance_fs_bg(struct f2fs_sb_info *); void f2fs_balance_fs_bg(struct f2fs_sb_info *);
int f2fs_issue_flush(struct f2fs_sb_info *); int f2fs_issue_flush(struct f2fs_sb_info *);
@ -1832,16 +1823,17 @@ void write_meta_page(struct f2fs_sb_info *, struct page *);
void write_node_page(unsigned int, struct f2fs_io_info *); void write_node_page(unsigned int, struct f2fs_io_info *);
void write_data_page(struct dnode_of_data *, struct f2fs_io_info *); void write_data_page(struct dnode_of_data *, struct f2fs_io_info *);
void rewrite_data_page(struct f2fs_io_info *); void rewrite_data_page(struct f2fs_io_info *);
void __f2fs_replace_block(struct f2fs_sb_info *, struct f2fs_summary *,
block_t, block_t, bool, bool);
void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *,
block_t, block_t, unsigned char, bool); block_t, block_t, unsigned char, bool, bool);
void allocate_data_block(struct f2fs_sb_info *, struct page *, void allocate_data_block(struct f2fs_sb_info *, struct page *,
block_t, block_t *, struct f2fs_summary *, int); block_t, block_t *, struct f2fs_summary *, int);
void f2fs_wait_on_page_writeback(struct page *, enum page_type); void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool);
void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t); void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t);
void write_data_summaries(struct f2fs_sb_info *, block_t); void write_data_summaries(struct f2fs_sb_info *, block_t);
void write_node_summaries(struct f2fs_sb_info *, block_t); void write_node_summaries(struct f2fs_sb_info *, block_t);
int lookup_journal_in_cursum(struct f2fs_summary_block *, int lookup_journal_in_cursum(struct f2fs_journal *, int, unsigned int, int);
int, unsigned int, int);
void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *); void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *);
int build_segment_manager(struct f2fs_sb_info *); int build_segment_manager(struct f2fs_sb_info *);
void destroy_segment_manager(struct f2fs_sb_info *); void destroy_segment_manager(struct f2fs_sb_info *);
@ -1881,11 +1873,16 @@ void destroy_checkpoint_caches(void);
* data.c * data.c
*/ */
void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int);
void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *,
struct page *, nid_t, enum page_type, int);
void f2fs_flush_merged_bios(struct f2fs_sb_info *);
int f2fs_submit_page_bio(struct f2fs_io_info *); int f2fs_submit_page_bio(struct f2fs_io_info *);
void f2fs_submit_page_mbio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *);
void set_data_blkaddr(struct dnode_of_data *); void set_data_blkaddr(struct dnode_of_data *);
void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t);
int reserve_new_block(struct dnode_of_data *); int reserve_new_block(struct dnode_of_data *);
int f2fs_get_block(struct dnode_of_data *, pgoff_t); int f2fs_get_block(struct dnode_of_data *, pgoff_t);
ssize_t f2fs_preallocate_blocks(struct kiocb *, struct iov_iter *);
int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); int f2fs_reserve_block(struct dnode_of_data *, pgoff_t);
struct page *get_read_data_page(struct inode *, pgoff_t, int, bool); struct page *get_read_data_page(struct inode *, pgoff_t, int, bool);
struct page *find_data_page(struct inode *, pgoff_t); struct page *find_data_page(struct inode *, pgoff_t);
@ -1902,7 +1899,7 @@ int f2fs_release_page(struct page *, gfp_t);
*/ */
int start_gc_thread(struct f2fs_sb_info *); int start_gc_thread(struct f2fs_sb_info *);
void stop_gc_thread(struct f2fs_sb_info *); void stop_gc_thread(struct f2fs_sb_info *);
block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *); block_t start_bidx_of_node(unsigned int, struct inode *);
int f2fs_gc(struct f2fs_sb_info *, bool); int f2fs_gc(struct f2fs_sb_info *, bool);
void build_gc_manager(struct f2fs_sb_info *); void build_gc_manager(struct f2fs_sb_info *);
@ -2093,7 +2090,7 @@ int f2fs_convert_inline_inode(struct inode *);
int f2fs_write_inline_data(struct inode *, struct page *); int f2fs_write_inline_data(struct inode *, struct page *);
bool recover_inline_data(struct inode *, struct page *); bool recover_inline_data(struct inode *, struct page *);
struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct f2fs_dir_entry *find_in_inline_dir(struct inode *,
struct f2fs_filename *, struct page **); struct fscrypt_name *, struct page **);
struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **); struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
@ -2102,7 +2099,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
struct inode *, struct inode *); struct inode *, struct inode *);
bool f2fs_empty_inline_dir(struct inode *); bool f2fs_empty_inline_dir(struct inode *);
int f2fs_read_inline_dir(struct file *, struct dir_context *, int f2fs_read_inline_dir(struct file *, struct dir_context *,
struct f2fs_str *); struct fscrypt_str *);
int f2fs_inline_data_fiemap(struct inode *, int f2fs_inline_data_fiemap(struct inode *,
struct fiemap_extent_info *, __u64, __u64); struct fiemap_extent_info *, __u64, __u64);
@ -2132,13 +2129,9 @@ void destroy_extent_cache(void);
/* /*
* crypto support * crypto support
*/ */
static inline int f2fs_encrypted_inode(struct inode *inode) static inline bool f2fs_encrypted_inode(struct inode *inode)
{ {
#ifdef CONFIG_F2FS_FS_ENCRYPTION
return file_is_encrypt(inode); return file_is_encrypt(inode);
#else
return 0;
#endif
} }
static inline void f2fs_set_encrypted_inode(struct inode *inode) static inline void f2fs_set_encrypted_inode(struct inode *inode)
@ -2150,20 +2143,12 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode)
static inline bool f2fs_bio_encrypted(struct bio *bio) static inline bool f2fs_bio_encrypted(struct bio *bio)
{ {
#ifdef CONFIG_F2FS_FS_ENCRYPTION return bio->bi_private != NULL;
return unlikely(bio->bi_private != NULL);
#else
return false;
#endif
} }
static inline int f2fs_sb_has_crypto(struct super_block *sb) static inline int f2fs_sb_has_crypto(struct super_block *sb)
{ {
#ifdef CONFIG_F2FS_FS_ENCRYPTION
return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
#else
return 0;
#endif
} }
static inline bool f2fs_may_encrypt(struct inode *inode) static inline bool f2fs_may_encrypt(struct inode *inode)
@ -2177,86 +2162,28 @@ static inline bool f2fs_may_encrypt(struct inode *inode)
#endif #endif
} }
/* crypto_policy.c */ #ifndef CONFIG_F2FS_FS_ENCRYPTION
int f2fs_is_child_context_consistent_with_parent(struct inode *, #define fscrypt_set_d_op(i)
struct inode *); #define fscrypt_get_ctx fscrypt_notsupp_get_ctx
int f2fs_inherit_context(struct inode *, struct inode *, struct page *); #define fscrypt_release_ctx fscrypt_notsupp_release_ctx
int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *); #define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page
int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *); #define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page
#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages
/* crypt.c */ #define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page
extern struct kmem_cache *f2fs_crypt_info_cachep; #define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page
bool f2fs_valid_contents_enc_mode(uint32_t); #define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range
uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t); #define fscrypt_process_policy fscrypt_notsupp_process_policy
struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *); #define fscrypt_get_policy fscrypt_notsupp_get_policy
void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *); #define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context
struct page *f2fs_encrypt(struct inode *, struct page *); #define fscrypt_inherit_context fscrypt_notsupp_inherit_context
int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *); #define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info
int f2fs_decrypt_one(struct inode *, struct page *); #define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info
void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *); #define fscrypt_setup_filename fscrypt_notsupp_setup_filename
#define fscrypt_free_filename fscrypt_notsupp_free_filename
/* crypto_key.c */ #define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size
void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *); #define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer
int _f2fs_get_encryption_info(struct inode *inode); #define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer
#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr
/* crypto_fname.c */ #define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
bool f2fs_valid_filenames_enc_mode(uint32_t);
u32 f2fs_fname_crypto_round_up(u32, u32);
int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *);
int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *,
const struct f2fs_str *, struct f2fs_str *);
int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *,
struct f2fs_str *);
#ifdef CONFIG_F2FS_FS_ENCRYPTION
void f2fs_restore_and_release_control_page(struct page **);
void f2fs_restore_control_page(struct page *);
int __init f2fs_init_crypto(void);
int f2fs_crypto_initialize(void);
void f2fs_exit_crypto(void);
int f2fs_has_encryption_key(struct inode *);
static inline int f2fs_get_encryption_info(struct inode *inode)
{
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
if (!ci ||
(ci->ci_keyring_key &&
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED) |
(1 << KEY_FLAG_DEAD)))))
return _f2fs_get_encryption_info(inode);
return 0;
}
void f2fs_fname_crypto_free_buffer(struct f2fs_str *);
int f2fs_fname_setup_filename(struct inode *, const struct qstr *,
int lookup, struct f2fs_filename *);
void f2fs_fname_free_filename(struct f2fs_filename *);
#else
static inline void f2fs_restore_and_release_control_page(struct page **p) { }
static inline void f2fs_restore_control_page(struct page *p) { }
static inline int __init f2fs_init_crypto(void) { return 0; }
static inline void f2fs_exit_crypto(void) { }
static inline int f2fs_has_encryption_key(struct inode *i) { return 0; }
static inline int f2fs_get_encryption_info(struct inode *i) { return 0; }
static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { }
static inline int f2fs_fname_setup_filename(struct inode *dir,
const struct qstr *iname,
int lookup, struct f2fs_filename *fname)
{
memset(fname, 0, sizeof(struct f2fs_filename));
fname->usr_fname = iname;
fname->disk_name.name = (unsigned char *)iname->name;
fname->disk_name.len = iname->len;
return 0;
}
static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { }
#endif #endif
#endif #endif

View File

@ -1,151 +0,0 @@
/*
* linux/fs/f2fs/f2fs_crypto.h
*
* Copied from linux/fs/ext4/ext4_crypto.h
*
* Copyright (C) 2015, Google, Inc.
*
* This contains encryption header content for f2fs
*
* Written by Michael Halcrow, 2015.
* Modified by Jaegeuk Kim, 2015.
*/
#ifndef _F2FS_CRYPTO_H
#define _F2FS_CRYPTO_H
#include <linux/fs.h>
#define F2FS_KEY_DESCRIPTOR_SIZE 8
/* Policy provided via an ioctl on the topmost directory */
struct f2fs_encryption_policy {
char version;
char contents_encryption_mode;
char filenames_encryption_mode;
char flags;
char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
} __attribute__((__packed__));
#define F2FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
#define F2FS_KEY_DERIVATION_NONCE_SIZE 16
#define F2FS_POLICY_FLAGS_PAD_4 0x00
#define F2FS_POLICY_FLAGS_PAD_8 0x01
#define F2FS_POLICY_FLAGS_PAD_16 0x02
#define F2FS_POLICY_FLAGS_PAD_32 0x03
#define F2FS_POLICY_FLAGS_PAD_MASK 0x03
#define F2FS_POLICY_FLAGS_VALID 0x03
/**
* 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 f2fs_encryption_context {
char format;
char contents_encryption_mode;
char filenames_encryption_mode;
char flags;
char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
char nonce[F2FS_KEY_DERIVATION_NONCE_SIZE];
} __attribute__((__packed__));
/* Encryption parameters */
#define F2FS_XTS_TWEAK_SIZE 16
#define F2FS_AES_128_ECB_KEY_SIZE 16
#define F2FS_AES_256_GCM_KEY_SIZE 32
#define F2FS_AES_256_CBC_KEY_SIZE 32
#define F2FS_AES_256_CTS_KEY_SIZE 32
#define F2FS_AES_256_XTS_KEY_SIZE 64
#define F2FS_MAX_KEY_SIZE 64
#define F2FS_KEY_DESC_PREFIX "f2fs:"
#define F2FS_KEY_DESC_PREFIX_SIZE 5
struct f2fs_encryption_key {
__u32 mode;
char raw[F2FS_MAX_KEY_SIZE];
__u32 size;
} __attribute__((__packed__));
struct f2fs_crypt_info {
char ci_data_mode;
char ci_filename_mode;
char ci_flags;
struct crypto_skcipher *ci_ctfm;
struct key *ci_keyring_key;
char ci_master_key[F2FS_KEY_DESCRIPTOR_SIZE];
};
#define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
#define F2FS_WRITE_PATH_FL 0x00000002
struct f2fs_crypto_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 */
};
char flags; /* Flags */
};
struct f2fs_completion_result {
struct completion completion;
int res;
};
#define DECLARE_F2FS_COMPLETION_RESULT(ecr) \
struct f2fs_completion_result ecr = { \
COMPLETION_INITIALIZER((ecr).completion), 0 }
static inline int f2fs_encryption_key_size(int mode)
{
switch (mode) {
case F2FS_ENCRYPTION_MODE_AES_256_XTS:
return F2FS_AES_256_XTS_KEY_SIZE;
case F2FS_ENCRYPTION_MODE_AES_256_GCM:
return F2FS_AES_256_GCM_KEY_SIZE;
case F2FS_ENCRYPTION_MODE_AES_256_CBC:
return F2FS_AES_256_CBC_KEY_SIZE;
case F2FS_ENCRYPTION_MODE_AES_256_CTS:
return F2FS_AES_256_CTS_KEY_SIZE;
default:
BUG();
}
return 0;
}
#define F2FS_FNAME_NUM_SCATTER_ENTRIES 4
#define F2FS_CRYPTO_BLOCK_SIZE 16
#define F2FS_FNAME_CRYPTO_DIGEST_SIZE 32
/**
* For encrypted symlinks, the ciphertext length is stored at the beginning
* of the string in little-endian format.
*/
struct f2fs_encrypted_symlink_data {
__le16 len;
char encrypted_path[1];
} __attribute__((__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 encrypted_symlink_data_len(u32 l)
{
return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1);
}
#endif /* _F2FS_CRYPTO_H */

View File

@ -86,7 +86,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
trace_f2fs_vm_page_mkwrite(page, DATA); trace_f2fs_vm_page_mkwrite(page, DATA);
mapped: mapped:
/* fill the page */ /* fill the page */
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, false);
/* wait for GCed encrypted page writeback */ /* wait for GCed encrypted page writeback */
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
@ -301,7 +301,7 @@ static pgoff_t __get_first_dirty_index(struct address_space *mapping,
pagevec_init(&pvec, 0); pagevec_init(&pvec, 0);
nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs, nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs,
PAGECACHE_TAG_DIRTY, 1); PAGECACHE_TAG_DIRTY, 1);
pgofs = nr_pages ? pvec.pages[0]->index : LONG_MAX; pgofs = nr_pages ? pvec.pages[0]->index : ULONG_MAX;
pagevec_release(&pvec); pagevec_release(&pvec);
return pgofs; return pgofs;
} }
@ -358,15 +358,14 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
} else if (err == -ENOENT) { } else if (err == -ENOENT) {
/* direct node does not exists */ /* direct node does not exists */
if (whence == SEEK_DATA) { if (whence == SEEK_DATA) {
pgofs = PGOFS_OF_NEXT_DNODE(pgofs, pgofs = get_next_page_offset(&dn, pgofs);
F2FS_I(inode));
continue; continue;
} else { } else {
goto found; goto found;
} }
} }
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
/* find data/hole in dnode block */ /* find data/hole in dnode block */
for (; dn.ofs_in_node < end_offset; for (; dn.ofs_in_node < end_offset;
@ -422,9 +421,11 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
int err; int err;
if (f2fs_encrypted_inode(inode)) { if (f2fs_encrypted_inode(inode)) {
err = f2fs_get_encryption_info(inode); err = fscrypt_get_encryption_info(inode);
if (err) if (err)
return 0; return 0;
if (!f2fs_encrypted_inode(inode))
return -ENOKEY;
} }
/* we don't need to use inline_data strictly */ /* we don't need to use inline_data strictly */
@ -440,12 +441,18 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
static int f2fs_file_open(struct inode *inode, struct file *filp) static int f2fs_file_open(struct inode *inode, struct file *filp)
{ {
int ret = generic_file_open(inode, filp); int ret = generic_file_open(inode, filp);
struct inode *dir = filp->f_path.dentry->d_parent->d_inode;
if (!ret && f2fs_encrypted_inode(inode)) { if (!ret && f2fs_encrypted_inode(inode)) {
ret = f2fs_get_encryption_info(inode); ret = fscrypt_get_encryption_info(inode);
if (ret) if (ret)
ret = -EACCES; return -EACCES;
if (!fscrypt_has_encryption_key(inode))
return -ENOKEY;
} }
if (f2fs_encrypted_inode(dir) &&
!fscrypt_has_permitted_context(dir, inode))
return -EPERM;
return ret; return ret;
} }
@ -480,7 +487,7 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
* we will invalidate all blkaddr in the whole range. * we will invalidate all blkaddr in the whole range.
*/ */
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fofs = start_bidx_of_node(ofs_of_node(dn->node_page),
F2FS_I(dn->inode)) + ofs; dn->inode) + ofs;
f2fs_update_extent_cache_range(dn, fofs, 0, len); f2fs_update_extent_cache_range(dn, fofs, 0, len);
dec_valid_block_count(sbi, dn->inode, nr_free); dec_valid_block_count(sbi, dn->inode, nr_free);
sync_inode_page(dn); sync_inode_page(dn);
@ -521,9 +528,10 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
if (IS_ERR(page)) if (IS_ERR(page))
return 0; return 0;
truncate_out: truncate_out:
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, true);
zero_user(page, offset, PAGE_CACHE_SIZE - offset); zero_user(page, offset, PAGE_CACHE_SIZE - offset);
if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) if (!cache_only || !f2fs_encrypted_inode(inode) ||
!S_ISREG(inode->i_mode))
set_page_dirty(page); set_page_dirty(page);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
return 0; return 0;
@ -568,7 +576,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
goto out; goto out;
} }
count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); count = ADDRS_PER_PAGE(dn.node_page, inode);
count -= dn.ofs_in_node; count -= dn.ofs_in_node;
f2fs_bug_on(sbi, count < 0); f2fs_bug_on(sbi, count < 0);
@ -671,7 +679,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_valid & ATTR_SIZE) {
if (f2fs_encrypted_inode(inode) && if (f2fs_encrypted_inode(inode) &&
f2fs_get_encryption_info(inode)) fscrypt_get_encryption_info(inode))
return -EACCES; return -EACCES;
if (attr->ia_size <= i_size_read(inode)) { if (attr->ia_size <= i_size_read(inode)) {
@ -743,7 +751,7 @@ static int fill_zero(struct inode *inode, pgoff_t index,
if (IS_ERR(page)) if (IS_ERR(page))
return PTR_ERR(page); return PTR_ERR(page);
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, true);
zero_user(page, start, len); zero_user(page, start, len);
set_page_dirty(page); set_page_dirty(page);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
@ -768,7 +776,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
return err; return err;
} }
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
count = min(end_offset - dn.ofs_in_node, pg_end - pg_start); count = min(end_offset - dn.ofs_in_node, pg_end - pg_start);
f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset); f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset);
@ -854,10 +862,8 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
} else { } else {
new_addr = dn.data_blkaddr; new_addr = dn.data_blkaddr;
if (!is_checkpointed_data(sbi, new_addr)) { if (!is_checkpointed_data(sbi, new_addr)) {
dn.data_blkaddr = NULL_ADDR;
/* do not invalidate this block address */ /* do not invalidate this block address */
set_data_blkaddr(&dn); f2fs_update_data_blkaddr(&dn, NULL_ADDR);
f2fs_update_extent_cache(&dn);
do_replace = true; do_replace = true;
} }
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
@ -884,7 +890,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
get_node_info(sbi, dn.nid, &ni); get_node_info(sbi, dn.nid, &ni);
f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr, f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr,
ni.version, true); ni.version, true, false);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
} else { } else {
struct page *psrc, *pdst; struct page *psrc, *pdst;
@ -892,7 +898,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
psrc = get_lock_data_page(inode, src, true); psrc = get_lock_data_page(inode, src, true);
if (IS_ERR(psrc)) if (IS_ERR(psrc))
return PTR_ERR(psrc); return PTR_ERR(psrc);
pdst = get_new_data_page(inode, NULL, dst, false); pdst = get_new_data_page(inode, NULL, dst, true);
if (IS_ERR(pdst)) { if (IS_ERR(pdst)) {
f2fs_put_page(psrc, 1); f2fs_put_page(psrc, 1);
return PTR_ERR(pdst); return PTR_ERR(pdst);
@ -908,9 +914,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
err_out: err_out:
if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) { if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) {
dn.data_blkaddr = new_addr; f2fs_update_data_blkaddr(&dn, new_addr);
set_data_blkaddr(&dn);
f2fs_update_extent_cache(&dn);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
} }
return ret; return ret;
@ -1050,12 +1054,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
if (dn.data_blkaddr != NEW_ADDR) { if (dn.data_blkaddr != NEW_ADDR) {
invalidate_blocks(sbi, dn.data_blkaddr); invalidate_blocks(sbi, dn.data_blkaddr);
f2fs_update_data_blkaddr(&dn, NEW_ADDR);
dn.data_blkaddr = NEW_ADDR;
set_data_blkaddr(&dn);
dn.data_blkaddr = NULL_ADDR;
f2fs_update_extent_cache(&dn);
} }
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
@ -1253,7 +1252,7 @@ static int f2fs_release_file(struct inode *inode, struct file *filp)
{ {
/* some remained atomic pages should discarded */ /* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode)) if (f2fs_is_atomic_file(inode))
commit_inmem_pages(inode, true); drop_inmem_pages(inode);
if (f2fs_is_volatile_file(inode)) { if (f2fs_is_volatile_file(inode)) {
set_inode_flag(F2FS_I(inode), FI_DROP_CACHE); set_inode_flag(F2FS_I(inode), FI_DROP_CACHE);
filemap_fdatawrite(inode->i_mapping); filemap_fdatawrite(inode->i_mapping);
@ -1377,7 +1376,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
if (f2fs_is_atomic_file(inode)) { if (f2fs_is_atomic_file(inode)) {
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
ret = commit_inmem_pages(inode, false); ret = commit_inmem_pages(inode);
if (ret) { if (ret) {
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
goto err_out; goto err_out;
@ -1440,7 +1439,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
if (f2fs_is_atomic_file(inode)) { if (f2fs_is_atomic_file(inode)) {
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
commit_inmem_pages(inode, true); drop_inmem_pages(inode);
} }
if (f2fs_is_volatile_file(inode)) { if (f2fs_is_volatile_file(inode)) {
clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
@ -1535,39 +1534,30 @@ static bool uuid_is_nonzero(__u8 u[16])
static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
{ {
#ifdef CONFIG_F2FS_FS_ENCRYPTION struct fscrypt_policy policy;
struct f2fs_encryption_policy policy;
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg, if (copy_from_user(&policy, (struct fscrypt_policy __user *)arg,
sizeof(policy))) sizeof(policy)))
return -EFAULT; return -EFAULT;
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return f2fs_process_policy(&policy, inode); return fscrypt_process_policy(inode, &policy);
#else
return -EOPNOTSUPP;
#endif
} }
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
{ {
#ifdef CONFIG_F2FS_FS_ENCRYPTION struct fscrypt_policy policy;
struct f2fs_encryption_policy policy;
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
int err; int err;
err = f2fs_get_policy(inode, &policy); err = fscrypt_get_policy(inode, &policy);
if (err) if (err)
return err; return err;
if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy, if (copy_to_user((struct fscrypt_policy __user *)arg, &policy, sizeof(policy)))
sizeof(policy)))
return -EFAULT; return -EFAULT;
return 0; return 0;
#else
return -EOPNOTSUPP;
#endif
} }
static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
@ -1648,7 +1638,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
struct f2fs_defragment *range) struct f2fs_defragment *range)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct f2fs_map_blocks map; struct f2fs_map_blocks map = { .m_next_pgofs = NULL };
struct extent_info ei; struct extent_info ei;
pgoff_t pg_start, pg_end; pgoff_t pg_start, pg_end;
unsigned int blk_per_seg = sbi->blocks_per_seg; unsigned int blk_per_seg = sbi->blocks_per_seg;
@ -1874,14 +1864,32 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{ {
struct inode *inode = file_inode(iocb->ki_filp); struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
ssize_t ret;
if (f2fs_encrypted_inode(inode) && if (f2fs_encrypted_inode(inode) &&
!f2fs_has_encryption_key(inode) && !fscrypt_has_encryption_key(inode) &&
f2fs_get_encryption_info(inode)) fscrypt_get_encryption_info(inode))
return -EACCES; return -EACCES;
return generic_file_write_iter(iocb, from); inode_lock(inode);
ret = generic_write_checks(iocb, from);
if (ret > 0) {
ret = f2fs_preallocate_blocks(iocb, from);
if (!ret)
ret = __generic_file_write_iter(iocb, from);
}
inode_unlock(inode);
if (ret > 0) {
ssize_t err;
err = generic_write_sync(file, iocb->ki_pos - ret, ret);
if (err < 0)
ret = err;
}
return ret;
} }
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT

View File

@ -245,6 +245,18 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
return get_cb_cost(sbi, segno); return get_cb_cost(sbi, segno);
} }
static unsigned int count_bits(const unsigned long *addr,
unsigned int offset, unsigned int len)
{
unsigned int end = offset + len, sum = 0;
while (offset < end) {
if (test_bit(offset++, addr))
++sum;
}
return sum;
}
/* /*
* This function is called from two paths. * This function is called from two paths.
* One is garbage collection and the other is SSR segment selection. * One is garbage collection and the other is SSR segment selection.
@ -258,9 +270,9 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
struct victim_sel_policy p; struct victim_sel_policy p;
unsigned int secno, max_cost; unsigned int secno, max_cost, last_victim;
unsigned int last_segment = MAIN_SEGS(sbi); unsigned int last_segment = MAIN_SEGS(sbi);
int nsearched = 0; unsigned int nsearched = 0;
mutex_lock(&dirty_i->seglist_lock); mutex_lock(&dirty_i->seglist_lock);
@ -273,6 +285,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
if (p.max_search == 0) if (p.max_search == 0)
goto out; goto out;
last_victim = sbi->last_victim[p.gc_mode];
if (p.alloc_mode == LFS && gc_type == FG_GC) { if (p.alloc_mode == LFS && gc_type == FG_GC) {
p.min_segno = check_bg_victims(sbi); p.min_segno = check_bg_victims(sbi);
if (p.min_segno != NULL_SEGNO) if (p.min_segno != NULL_SEGNO)
@ -295,27 +308,35 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
} }
p.offset = segno + p.ofs_unit; p.offset = segno + p.ofs_unit;
if (p.ofs_unit > 1) if (p.ofs_unit > 1) {
p.offset -= segno % p.ofs_unit; p.offset -= segno % p.ofs_unit;
nsearched += count_bits(p.dirty_segmap,
p.offset - p.ofs_unit,
p.ofs_unit);
} else {
nsearched++;
}
secno = GET_SECNO(sbi, segno); secno = GET_SECNO(sbi, segno);
if (sec_usage_check(sbi, secno)) if (sec_usage_check(sbi, secno))
continue; goto next;
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap)) if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
continue; goto next;
cost = get_gc_cost(sbi, segno, &p); cost = get_gc_cost(sbi, segno, &p);
if (p.min_cost > cost) { if (p.min_cost > cost) {
p.min_segno = segno; p.min_segno = segno;
p.min_cost = cost; p.min_cost = cost;
} else if (unlikely(cost == max_cost)) {
continue;
} }
next:
if (nsearched++ >= p.max_search) { if (nsearched >= p.max_search) {
sbi->last_victim[p.gc_mode] = segno; if (!sbi->last_victim[p.gc_mode] && segno <= last_victim)
sbi->last_victim[p.gc_mode] = last_victim + 1;
else
sbi->last_victim[p.gc_mode] = segno + 1;
break; break;
} }
} }
@ -399,7 +420,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi,
* On validity, copy that node with cold status, otherwise (invalid node) * On validity, copy that node with cold status, otherwise (invalid node)
* ignore that. * ignore that.
*/ */
static int gc_node_segment(struct f2fs_sb_info *sbi, static void gc_node_segment(struct f2fs_sb_info *sbi,
struct f2fs_summary *sum, unsigned int segno, int gc_type) struct f2fs_summary *sum, unsigned int segno, int gc_type)
{ {
bool initial = true; bool initial = true;
@ -419,7 +440,7 @@ next_step:
/* stop BG_GC if there is not enough free sections. */ /* stop BG_GC if there is not enough free sections. */
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
return 0; return;
if (check_valid_map(sbi, segno, off) == 0) if (check_valid_map(sbi, segno, off) == 0)
continue; continue;
@ -446,7 +467,7 @@ next_step:
/* set page dirty and write it */ /* set page dirty and write it */
if (gc_type == FG_GC) { if (gc_type == FG_GC) {
f2fs_wait_on_page_writeback(node_page, NODE); f2fs_wait_on_page_writeback(node_page, NODE, true);
set_page_dirty(node_page); set_page_dirty(node_page);
} else { } else {
if (!PageWriteback(node_page)) if (!PageWriteback(node_page))
@ -460,20 +481,6 @@ next_step:
initial = false; initial = false;
goto next_step; goto next_step;
} }
if (gc_type == FG_GC) {
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = LONG_MAX,
.for_reclaim = 0,
};
sync_node_pages(sbi, 0, &wbc);
/* return 1 only if FG_GC succefully reclaimed one */
if (get_valid_blocks(sbi, segno, 1) == 0)
return 1;
}
return 0;
} }
/* /*
@ -483,7 +490,7 @@ next_step:
* as indirect or double indirect node blocks, are given, it must be a caller's * as indirect or double indirect node blocks, are given, it must be a caller's
* bug. * bug.
*/ */
block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi) block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode)
{ {
unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4; unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
unsigned int bidx; unsigned int bidx;
@ -500,7 +507,7 @@ block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi)
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
bidx = node_ofs - 5 - dec; bidx = node_ofs - 5 - dec;
} }
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi); return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode);
} }
static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
@ -546,6 +553,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
struct f2fs_summary sum; struct f2fs_summary sum;
struct node_info ni; struct node_info ni;
struct page *page; struct page *page;
block_t newaddr;
int err; int err;
/* do not read out */ /* do not read out */
@ -567,21 +575,24 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
* don't cache encrypted data into meta inode until previous dirty * don't cache encrypted data into meta inode until previous dirty
* data were writebacked to avoid racing between GC and flush. * data were writebacked to avoid racing between GC and flush.
*/ */
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, true);
get_node_info(fio.sbi, dn.nid, &ni); get_node_info(fio.sbi, dn.nid, &ni);
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
/* read page */ /* read page */
fio.page = page; fio.page = page;
fio.blk_addr = dn.data_blkaddr; fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
fio.blk_addr, &sum, CURSEG_COLD_DATA);
FGP_LOCK|FGP_CREAT,
GFP_NOFS); fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
if (!fio.encrypted_page) FGP_LOCK | FGP_CREAT, GFP_NOFS);
goto put_out; if (!fio.encrypted_page) {
err = -ENOMEM;
goto recover_block;
}
err = f2fs_submit_page_bio(&fio); err = f2fs_submit_page_bio(&fio);
if (err) if (err)
@ -590,33 +601,39 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
/* write page */ /* write page */
lock_page(fio.encrypted_page); lock_page(fio.encrypted_page);
if (unlikely(!PageUptodate(fio.encrypted_page))) if (unlikely(!PageUptodate(fio.encrypted_page))) {
err = -EIO;
goto put_page_out; goto put_page_out;
if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) }
if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) {
err = -EIO;
goto put_page_out; goto put_page_out;
}
set_page_dirty(fio.encrypted_page); set_page_dirty(fio.encrypted_page);
f2fs_wait_on_page_writeback(fio.encrypted_page, DATA); f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true);
if (clear_page_dirty_for_io(fio.encrypted_page)) if (clear_page_dirty_for_io(fio.encrypted_page))
dec_page_count(fio.sbi, F2FS_DIRTY_META); dec_page_count(fio.sbi, F2FS_DIRTY_META);
set_page_writeback(fio.encrypted_page); set_page_writeback(fio.encrypted_page);
/* allocate block address */ /* allocate block address */
f2fs_wait_on_page_writeback(dn.node_page, NODE); f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
allocate_data_block(fio.sbi, NULL, fio.blk_addr,
&fio.blk_addr, &sum, CURSEG_COLD_DATA);
fio.rw = WRITE_SYNC; fio.rw = WRITE_SYNC;
fio.new_blkaddr = newaddr;
f2fs_submit_page_mbio(&fio); f2fs_submit_page_mbio(&fio);
dn.data_blkaddr = fio.blk_addr; f2fs_update_data_blkaddr(&dn, newaddr);
set_data_blkaddr(&dn);
f2fs_update_extent_cache(&dn);
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
if (page->index == 0) if (page->index == 0)
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
put_page_out: put_page_out:
f2fs_put_page(fio.encrypted_page, 1); f2fs_put_page(fio.encrypted_page, 1);
recover_block:
if (err)
__f2fs_replace_block(fio.sbi, &sum, newaddr, fio.old_blkaddr,
true, true);
put_out: put_out:
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
out: out:
@ -645,7 +662,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
.encrypted_page = NULL, .encrypted_page = NULL,
}; };
set_page_dirty(page); set_page_dirty(page);
f2fs_wait_on_page_writeback(page, DATA); 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); inode_dec_dirty_pages(inode);
set_cold_data(page); set_cold_data(page);
@ -663,7 +680,7 @@ out:
* If the parent node is not valid or the data block address is different, * If the parent node is not valid or the data block address is different,
* the victim data block is ignored. * the victim data block is ignored.
*/ */
static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
struct gc_inode_list *gc_list, unsigned int segno, int gc_type) struct gc_inode_list *gc_list, unsigned int segno, int gc_type)
{ {
struct super_block *sb = sbi->sb; struct super_block *sb = sbi->sb;
@ -686,7 +703,7 @@ next_step:
/* stop BG_GC if there is not enough free sections. */ /* stop BG_GC if there is not enough free sections. */
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
return 0; return;
if (check_valid_map(sbi, segno, off) == 0) if (check_valid_map(sbi, segno, off) == 0)
continue; continue;
@ -719,7 +736,7 @@ next_step:
continue; continue;
} }
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); start_bidx = start_bidx_of_node(nofs, inode);
data_page = get_read_data_page(inode, data_page = get_read_data_page(inode,
start_bidx + ofs_in_node, READA, true); start_bidx + ofs_in_node, READA, true);
if (IS_ERR(data_page)) { if (IS_ERR(data_page)) {
@ -735,7 +752,7 @@ next_step:
/* phase 3 */ /* phase 3 */
inode = find_gc_inode(gc_list, dni.ino); inode = find_gc_inode(gc_list, dni.ino);
if (inode) { if (inode) {
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)) start_bidx = start_bidx_of_node(nofs, inode)
+ ofs_in_node; + ofs_in_node;
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
move_encrypted_block(inode, start_bidx); move_encrypted_block(inode, start_bidx);
@ -747,15 +764,6 @@ next_step:
if (++phase < 4) if (++phase < 4)
goto next_step; goto next_step;
if (gc_type == FG_GC) {
f2fs_submit_merged_bio(sbi, DATA, WRITE);
/* return 1 only if FG_GC succefully reclaimed one */
if (get_valid_blocks(sbi, segno, 1) == 0)
return 1;
}
return 0;
} }
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
@ -771,53 +779,92 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
return ret; return ret;
} }
static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, static int do_garbage_collect(struct f2fs_sb_info *sbi,
unsigned int start_segno,
struct gc_inode_list *gc_list, int gc_type) struct gc_inode_list *gc_list, int gc_type)
{ {
struct page *sum_page; struct page *sum_page;
struct f2fs_summary_block *sum; struct f2fs_summary_block *sum;
struct blk_plug plug; struct blk_plug plug;
int nfree = 0; unsigned int segno = start_segno;
unsigned int end_segno = start_segno + sbi->segs_per_sec;
int seg_freed = 0;
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
SUM_TYPE_DATA : SUM_TYPE_NODE;
/* read segment summary of victim */ /* readahead multi ssa blocks those have contiguous address */
sum_page = get_sum_page(sbi, segno); if (sbi->segs_per_sec > 1)
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
sbi->segs_per_sec, META_SSA, true);
/* reference all summary page */
while (segno < end_segno) {
sum_page = get_sum_page(sbi, segno++);
unlock_page(sum_page);
}
blk_start_plug(&plug); blk_start_plug(&plug);
sum = page_address(sum_page); for (segno = start_segno; segno < end_segno; segno++) {
/* find segment summary of victim */
sum_page = find_get_page(META_MAPPING(sbi),
GET_SUM_BLOCK(sbi, segno));
f2fs_bug_on(sbi, !PageUptodate(sum_page));
f2fs_put_page(sum_page, 0);
/* sum = page_address(sum_page);
* this is to avoid deadlock: f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer)));
* - lock_page(sum_page) - f2fs_replace_block
* - check_valid_map() - mutex_lock(sentry_lock)
* - mutex_lock(sentry_lock) - change_curseg()
* - lock_page(sum_page)
*/
unlock_page(sum_page);
switch (GET_SUM_TYPE((&sum->footer))) { /*
case SUM_TYPE_NODE: * this is to avoid deadlock:
nfree = gc_node_segment(sbi, sum->entries, segno, gc_type); * - lock_page(sum_page) - f2fs_replace_block
break; * - check_valid_map() - mutex_lock(sentry_lock)
case SUM_TYPE_DATA: * - mutex_lock(sentry_lock) - change_curseg()
nfree = gc_data_segment(sbi, sum->entries, gc_list, * - lock_page(sum_page)
segno, gc_type); */
break;
if (type == SUM_TYPE_NODE)
gc_node_segment(sbi, sum->entries, segno, gc_type);
else
gc_data_segment(sbi, sum->entries, gc_list, segno,
gc_type);
stat_inc_seg_count(sbi, type, gc_type);
f2fs_put_page(sum_page, 0);
} }
if (gc_type == FG_GC) {
if (type == SUM_TYPE_NODE) {
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = LONG_MAX,
.for_reclaim = 0,
};
sync_node_pages(sbi, 0, &wbc);
} else {
f2fs_submit_merged_bio(sbi, DATA, WRITE);
}
}
blk_finish_plug(&plug); blk_finish_plug(&plug);
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type); if (gc_type == FG_GC) {
while (start_segno < end_segno)
if (get_valid_blocks(sbi, start_segno++, 1) == 0)
seg_freed++;
}
stat_inc_call_count(sbi->stat_info); stat_inc_call_count(sbi->stat_info);
f2fs_put_page(sum_page, 0); return seg_freed;
return nfree;
} }
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync) int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
{ {
unsigned int segno, i; unsigned int segno;
int gc_type = sync ? FG_GC : BG_GC; int gc_type = sync ? FG_GC : BG_GC;
int sec_freed = 0; int sec_freed = 0, seg_freed;
int ret = -EINVAL; int ret = -EINVAL;
struct cp_control cpc; struct cp_control cpc;
struct gc_inode_list gc_list = { struct gc_inode_list gc_list = {
@ -838,30 +885,24 @@ gc_more:
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) { if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) {
gc_type = FG_GC; gc_type = FG_GC;
/*
* 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.
*/
if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi)) if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi))
write_checkpoint(sbi, &cpc); write_checkpoint(sbi, &cpc);
else if (has_not_enough_free_secs(sbi, 0))
write_checkpoint(sbi, &cpc);
} }
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type)) if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
goto stop; goto stop;
ret = 0; ret = 0;
/* readahead multi ssa blocks those have contiguous address */ seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
if (sbi->segs_per_sec > 1)
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec,
META_SSA, true);
for (i = 0; i < sbi->segs_per_sec; i++) { if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
/*
* for FG_GC case, halt gcing left segments once failed one
* of segments in selected section to avoid long latency.
*/
if (!do_garbage_collect(sbi, segno + i, &gc_list, gc_type) &&
gc_type == FG_GC)
break;
}
if (i == sbi->segs_per_sec && gc_type == FG_GC)
sec_freed++; sec_freed++;
if (gc_type == FG_GC) if (gc_type == FG_GC)

View File

@ -71,7 +71,7 @@ bool truncate_inline_inode(struct page *ipage, u64 from)
addr = inline_data_addr(ipage); addr = inline_data_addr(ipage);
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
memset(addr + from, 0, MAX_INLINE_DATA - from); memset(addr + from, 0, MAX_INLINE_DATA - from);
return true; return true;
@ -105,7 +105,6 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
{ {
void *src_addr, *dst_addr;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(dn->inode), .sbi = F2FS_I_SB(dn->inode),
.type = DATA, .type = DATA,
@ -115,8 +114,6 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
}; };
int dirty, err; int dirty, err;
f2fs_bug_on(F2FS_I_SB(dn->inode), page->index);
if (!f2fs_exist_data(dn->inode)) if (!f2fs_exist_data(dn->inode))
goto clear_out; goto clear_out;
@ -124,21 +121,9 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
if (err) if (err)
return err; return err;
f2fs_wait_on_page_writeback(page, DATA); f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page));
if (PageUptodate(page)) read_inline_data(page, dn->inode_page);
goto no_update;
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
/* Copy the whole inline data block */
src_addr = inline_data_addr(dn->inode_page);
dst_addr = kmap_atomic(page);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
flush_dcache_page(page);
kunmap_atomic(dst_addr);
SetPageUptodate(page);
no_update:
set_page_dirty(page); set_page_dirty(page);
/* clear dirty state */ /* clear dirty state */
@ -146,11 +131,9 @@ no_update:
/* write data page to try to make data consistent */ /* write data page to try to make data consistent */
set_page_writeback(page); set_page_writeback(page);
fio.blk_addr = dn->data_blkaddr; fio.old_blkaddr = dn->data_blkaddr;
write_data_page(dn, &fio); write_data_page(dn, &fio);
set_data_blkaddr(dn); f2fs_wait_on_page_writeback(page, DATA, true);
f2fs_update_extent_cache(dn);
f2fs_wait_on_page_writeback(page, DATA);
if (dirty) if (dirty)
inode_dec_dirty_pages(dn->inode); inode_dec_dirty_pages(dn->inode);
@ -159,6 +142,7 @@ no_update:
/* clear inline data and flag after data writeback */ /* clear inline data and flag after data writeback */
truncate_inline_inode(dn->inode_page, 0); truncate_inline_inode(dn->inode_page, 0);
clear_inline_node(dn->inode_page);
clear_out: clear_out:
stat_dec_inline_inode(dn->inode); stat_dec_inline_inode(dn->inode);
f2fs_clear_inline_inode(dn->inode); f2fs_clear_inline_inode(dn->inode);
@ -223,7 +207,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
f2fs_bug_on(F2FS_I_SB(inode), page->index); f2fs_bug_on(F2FS_I_SB(inode), page->index);
f2fs_wait_on_page_writeback(dn.inode_page, NODE); f2fs_wait_on_page_writeback(dn.inode_page, NODE, true);
src_addr = kmap_atomic(page); src_addr = kmap_atomic(page);
dst_addr = inline_data_addr(dn.inode_page); dst_addr = inline_data_addr(dn.inode_page);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA); memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
@ -233,6 +217,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
sync_inode_page(&dn); sync_inode_page(&dn);
clear_inline_node(dn.inode_page);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
return 0; return 0;
} }
@ -261,7 +246,7 @@ process_inline:
ipage = get_node_page(sbi, inode->i_ino); ipage = get_node_page(sbi, inode->i_ino);
f2fs_bug_on(sbi, IS_ERR(ipage)); f2fs_bug_on(sbi, IS_ERR(ipage));
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
src_addr = inline_data_addr(npage); src_addr = inline_data_addr(npage);
dst_addr = inline_data_addr(ipage); dst_addr = inline_data_addr(ipage);
@ -292,7 +277,7 @@ process_inline:
} }
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
struct f2fs_filename *fname, struct page **res_page) struct fscrypt_name *fname, struct page **res_page)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
struct f2fs_inline_dentry *inline_dentry; struct f2fs_inline_dentry *inline_dentry;
@ -389,7 +374,7 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
if (err) if (err)
goto out; goto out;
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA, true);
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
dentry_blk = kmap_atomic(page); dentry_blk = kmap_atomic(page);
@ -469,7 +454,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
} }
} }
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
name_hash = f2fs_dentry_hash(name); name_hash = f2fs_dentry_hash(name);
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
@ -507,7 +492,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
int i; int i;
lock_page(page); lock_page(page);
f2fs_wait_on_page_writeback(page, NODE); f2fs_wait_on_page_writeback(page, NODE, true);
inline_dentry = inline_data_addr(page); inline_dentry = inline_data_addr(page);
bit_pos = dentry - inline_dentry->dentry; bit_pos = dentry - inline_dentry->dentry;
@ -550,7 +535,7 @@ bool f2fs_empty_inline_dir(struct inode *dir)
} }
int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
struct f2fs_str *fstr) struct fscrypt_str *fstr)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct f2fs_inline_dentry *inline_dentry = NULL; struct f2fs_inline_dentry *inline_dentry = NULL;

View File

@ -83,7 +83,7 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage)
while (start < end) { while (start < end) {
if (*start++) { if (*start++) {
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage)); set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage));
@ -227,7 +227,7 @@ int update_inode(struct inode *inode, struct page *node_page)
{ {
struct f2fs_inode *ri; struct f2fs_inode *ri;
f2fs_wait_on_page_writeback(node_page, NODE); f2fs_wait_on_page_writeback(node_page, NODE, true);
ri = F2FS_INODE(node_page); ri = F2FS_INODE(node_page);
@ -263,6 +263,10 @@ int update_inode(struct inode *inode, struct page *node_page)
set_cold_node(inode, node_page); set_cold_node(inode, node_page);
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
/* deleted inode */
if (inode->i_nlink == 0)
clear_inline_node(node_page);
return set_page_dirty(node_page); return set_page_dirty(node_page);
} }
@ -320,7 +324,7 @@ void f2fs_evict_inode(struct inode *inode)
/* some remained atomic pages should discarded */ /* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode)) if (f2fs_is_atomic_file(inode))
commit_inmem_pages(inode, true); drop_inmem_pages(inode);
trace_f2fs_evict_inode(inode); trace_f2fs_evict_inode(inode);
truncate_inode_pages_final(&inode->i_data); truncate_inode_pages_final(&inode->i_data);
@ -385,10 +389,7 @@ no_delete:
} }
} }
out_clear: out_clear:
#ifdef CONFIG_F2FS_FS_ENCRYPTION fscrypt_put_encryption_info(inode, NULL);
if (fi->i_crypt_info)
f2fs_free_encryption_info(inode, fi->i_crypt_info);
#endif
clear_inode(inode); clear_inode(inode);
} }

View File

@ -169,7 +169,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
int err; int err;
if (f2fs_encrypted_inode(dir) && if (f2fs_encrypted_inode(dir) &&
!f2fs_is_child_context_consistent_with_parent(dir, inode)) !fscrypt_has_permitted_context(dir, inode))
return -EPERM; return -EPERM;
f2fs_balance_fs(sbi, true); f2fs_balance_fs(sbi, true);
@ -260,6 +260,22 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
struct page *page; struct page *page;
nid_t ino; nid_t ino;
int err = 0; int err = 0;
unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir));
if (f2fs_encrypted_inode(dir)) {
int res = fscrypt_get_encryption_info(dir);
/*
* DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is
* created while the directory was encrypted and we
* don't have access to the key.
*/
if (fscrypt_has_encryption_key(dir))
fscrypt_set_encrypted_dentry(dentry);
fscrypt_set_d_op(dentry);
if (res && res != -ENOKEY)
return ERR_PTR(res);
}
if (dentry->d_name.len > F2FS_NAME_LEN) if (dentry->d_name.len > F2FS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
@ -276,15 +292,29 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(inode); return ERR_CAST(inode);
if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) {
err = __recover_dot_dentries(dir, root_ino);
if (err)
goto err_out;
}
if (f2fs_has_inline_dots(inode)) { if (f2fs_has_inline_dots(inode)) {
err = __recover_dot_dentries(inode, dir->i_ino); err = __recover_dot_dentries(inode, dir->i_ino);
if (err) if (err)
goto err_out; 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;
goto err_out;
}
return d_splice_alias(inode, dentry); return d_splice_alias(inode, dentry);
err_out: err_out:
iget_failed(inode); iput(inode);
return ERR_PTR(err); return ERR_PTR(err);
} }
@ -345,13 +375,23 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct inode *inode; struct inode *inode;
size_t len = strlen(symname); size_t len = strlen(symname);
size_t p_len; struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
char *p_str; struct fscrypt_symlink_data *sd = NULL;
struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
struct f2fs_encrypted_symlink_data *sd = NULL;
int err; int err;
if (len > dir->i_sb->s_blocksize) if (f2fs_encrypted_inode(dir)) {
err = fscrypt_get_encryption_info(dir);
if (err)
return err;
if (!fscrypt_has_encryption_key(dir))
return -EPERM;
disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
sizeof(struct fscrypt_symlink_data));
}
if (disk_link.len > dir->i_sb->s_blocksize)
return -ENAMETOOLONG; return -ENAMETOOLONG;
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
@ -374,42 +414,36 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
alloc_nid_done(sbi, inode->i_ino); alloc_nid_done(sbi, inode->i_ino);
if (f2fs_encrypted_inode(dir)) { if (f2fs_encrypted_inode(inode)) {
struct qstr istr = QSTR_INIT(symname, len); struct qstr istr = QSTR_INIT(symname, len);
struct fscrypt_str ostr;
err = f2fs_get_encryption_info(inode); sd = kzalloc(disk_link.len, GFP_NOFS);
if (err)
goto err_out;
err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link);
if (err)
goto err_out;
err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link);
if (err < 0)
goto err_out;
p_len = encrypted_symlink_data_len(disk_link.len) + 1;
if (p_len > dir->i_sb->s_blocksize) {
err = -ENAMETOOLONG;
goto err_out;
}
sd = kzalloc(p_len, GFP_NOFS);
if (!sd) { if (!sd) {
err = -ENOMEM; err = -ENOMEM;
goto err_out; goto err_out;
} }
memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
sd->len = cpu_to_le16(disk_link.len); err = fscrypt_get_encryption_info(inode);
p_str = (char *)sd; if (err)
} else { goto err_out;
p_len = len + 1;
p_str = (char *)symname; if (!fscrypt_has_encryption_key(inode)) {
err = -EPERM;
goto err_out;
}
ostr.name = sd->encrypted_path;
ostr.len = disk_link.len;
err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
if (err < 0)
goto err_out;
sd->len = cpu_to_le16(ostr.len);
disk_link.name = (char *)sd;
} }
err = page_symlink(inode, p_str, p_len); err = page_symlink(inode, disk_link.name, disk_link.len);
err_out: err_out:
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
@ -425,7 +459,8 @@ err_out:
* performance regression. * performance regression.
*/ */
if (!err) { if (!err) {
filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1); filemap_write_and_wait_range(inode->i_mapping, 0,
disk_link.len - 1);
if (IS_DIRSYNC(dir)) if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1); f2fs_sync_fs(sbi->sb, 1);
@ -434,7 +469,6 @@ err_out:
} }
kfree(sd); kfree(sd);
f2fs_fname_crypto_free_buffer(&disk_link);
return err; return err;
out: out:
handle_failed_inode(inode); handle_failed_inode(inode);
@ -582,7 +616,7 @@ out:
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{ {
if (f2fs_encrypted_inode(dir)) { if (f2fs_encrypted_inode(dir)) {
int err = f2fs_get_encryption_info(dir); int err = fscrypt_get_encryption_info(dir);
if (err) if (err)
return err; return err;
} }
@ -608,11 +642,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_dir_entry = NULL;
struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *old_entry;
struct f2fs_dir_entry *new_entry; struct f2fs_dir_entry *new_entry;
bool is_old_inline = f2fs_has_inline_dentry(old_dir);
int err = -ENOENT; int err = -ENOENT;
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
!f2fs_is_child_context_consistent_with_parent(new_dir, !fscrypt_has_permitted_context(new_dir, old_inode)) {
old_inode)) {
err = -EPERM; err = -EPERM;
goto out; goto out;
} }
@ -654,8 +688,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (err) if (err)
goto put_out_dir; goto put_out_dir;
if (update_dent_inode(old_inode, new_inode, err = update_dent_inode(old_inode, new_inode,
&new_dentry->d_name)) { &new_dentry->d_name);
if (err) {
release_orphan_inode(sbi); release_orphan_inode(sbi);
goto put_out_dir; goto put_out_dir;
} }
@ -693,6 +728,26 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
inc_nlink(new_dir); inc_nlink(new_dir);
update_inode_page(new_dir); update_inode_page(new_dir);
} }
/*
* old entry and new entry can locate in the same inline
* dentry in inode, when attaching new entry in inline dentry,
* it could force inline dentry conversion, after that,
* old_entry and old_page will point to wrong address, in
* order to avoid this, let's do the check and update here.
*/
if (is_old_inline && !f2fs_has_inline_dentry(old_dir)) {
f2fs_put_page(old_page, 0);
old_page = NULL;
old_entry = f2fs_find_entry(old_dir,
&old_dentry->d_name, &old_page);
if (!old_entry) {
err = -EIO;
f2fs_unlock_op(sbi);
goto out_whiteout;
}
}
} }
down_write(&F2FS_I(old_inode)->i_sem); down_write(&F2FS_I(old_inode)->i_sem);
@ -771,11 +826,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
int err = -ENOENT; int err = -ENOENT;
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
(old_dir != new_dir) && (old_dir != new_dir) &&
(!f2fs_is_child_context_consistent_with_parent(new_dir, (!fscrypt_has_permitted_context(new_dir, old_inode) ||
old_inode) || !fscrypt_has_permitted_context(old_dir, new_inode)))
!f2fs_is_child_context_consistent_with_parent(old_dir,
new_inode)))
return -EPERM; return -EPERM;
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
@ -937,16 +990,15 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
} }
#ifdef CONFIG_F2FS_FS_ENCRYPTION
static const char *f2fs_encrypted_get_link(struct dentry *dentry, static const char *f2fs_encrypted_get_link(struct dentry *dentry,
struct inode *inode, struct inode *inode,
struct delayed_call *done) struct delayed_call *done)
{ {
struct page *cpage = NULL; struct page *cpage = NULL;
char *caddr, *paddr = NULL; char *caddr, *paddr = NULL;
struct f2fs_str cstr = FSTR_INIT(NULL, 0); struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
struct f2fs_str pstr = FSTR_INIT(NULL, 0); struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
struct f2fs_encrypted_symlink_data *sd; struct fscrypt_symlink_data *sd;
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
u32 max_size = inode->i_sb->s_blocksize; u32 max_size = inode->i_sb->s_blocksize;
int res; int res;
@ -954,7 +1006,7 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
if (!dentry) if (!dentry)
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
res = f2fs_get_encryption_info(inode); res = fscrypt_get_encryption_info(inode);
if (res) if (res)
return ERR_PTR(res); return ERR_PTR(res);
@ -965,7 +1017,8 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
caddr[size] = 0; caddr[size] = 0;
/* Symlink is encrypted */ /* Symlink is encrypted */
sd = (struct f2fs_encrypted_symlink_data *)caddr; sd = (struct fscrypt_symlink_data *)caddr;
cstr.name = sd->encrypted_path;
cstr.len = le16_to_cpu(sd->len); cstr.len = le16_to_cpu(sd->len);
/* this is broken symlink case */ /* this is broken symlink case */
@ -973,12 +1026,6 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
res = -ENOENT; res = -ENOENT;
goto errout; goto errout;
} }
cstr.name = kmalloc(cstr.len, GFP_NOFS);
if (!cstr.name) {
res = -ENOMEM;
goto errout;
}
memcpy(cstr.name, sd->encrypted_path, cstr.len);
/* this is broken symlink case */ /* this is broken symlink case */
if (unlikely(cstr.name[0] == 0)) { if (unlikely(cstr.name[0] == 0)) {
@ -986,22 +1033,19 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
goto errout; goto errout;
} }
if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) > if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
max_size) {
/* Symlink data on the disk is corrupted */ /* Symlink data on the disk is corrupted */
res = -EIO; res = -EIO;
goto errout; goto errout;
} }
res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr); res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
if (res) if (res)
goto errout; goto errout;
res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr); res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
if (res < 0) if (res < 0)
goto errout; goto errout;
kfree(cstr.name);
paddr = pstr.name; paddr = pstr.name;
/* Null-terminate the name */ /* Null-terminate the name */
@ -1011,8 +1055,7 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
set_delayed_call(done, kfree_link, paddr); set_delayed_call(done, kfree_link, paddr);
return paddr; return paddr;
errout: errout:
kfree(cstr.name); fscrypt_fname_free_buffer(&pstr);
f2fs_fname_crypto_free_buffer(&pstr);
page_cache_release(cpage); page_cache_release(cpage);
return ERR_PTR(res); return ERR_PTR(res);
} }
@ -1029,7 +1072,6 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
#endif #endif
}; };
#endif
const struct inode_operations f2fs_dir_inode_operations = { const struct inode_operations f2fs_dir_inode_operations = {
.create = f2fs_create, .create = f2fs_create,

View File

@ -257,15 +257,20 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)
return new; return new;
} }
static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
struct f2fs_nat_entry *ne) struct f2fs_nat_entry *ne)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *e; struct nat_entry *e;
e = __lookup_nat_cache(nm_i, nid); e = __lookup_nat_cache(nm_i, nid);
if (!e) { if (!e) {
e = grab_nat_entry(nm_i, nid); e = grab_nat_entry(nm_i, nid);
node_info_from_raw_nat(&e->ni, ne); node_info_from_raw_nat(&e->ni, ne);
} else {
f2fs_bug_on(sbi, nat_get_ino(e) != ne->ino ||
nat_get_blkaddr(e) != ne->block_addr ||
nat_get_version(e) != ne->version);
} }
} }
@ -354,7 +359,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
nid_t start_nid = START_NID(nid); nid_t start_nid = START_NID(nid);
struct f2fs_nat_block *nat_blk; struct f2fs_nat_block *nat_blk;
struct page *page = NULL; struct page *page = NULL;
@ -371,23 +376,20 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
ni->ino = nat_get_ino(e); ni->ino = nat_get_ino(e);
ni->blk_addr = nat_get_blkaddr(e); ni->blk_addr = nat_get_blkaddr(e);
ni->version = nat_get_version(e); ni->version = nat_get_version(e);
} up_read(&nm_i->nat_tree_lock);
up_read(&nm_i->nat_tree_lock);
if (e)
return; return;
}
memset(&ne, 0, sizeof(struct f2fs_nat_entry)); memset(&ne, 0, sizeof(struct f2fs_nat_entry));
down_write(&nm_i->nat_tree_lock);
/* Check current segment summary */ /* Check current segment summary */
mutex_lock(&curseg->curseg_mutex); down_read(&curseg->journal_rwsem);
i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0); i = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0);
if (i >= 0) { if (i >= 0) {
ne = nat_in_journal(sum, i); ne = nat_in_journal(journal, i);
node_info_from_raw_nat(ni, &ne); node_info_from_raw_nat(ni, &ne);
} }
mutex_unlock(&curseg->curseg_mutex); up_read(&curseg->journal_rwsem);
if (i >= 0) if (i >= 0)
goto cache; goto cache;
@ -398,19 +400,52 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
node_info_from_raw_nat(ni, &ne); node_info_from_raw_nat(ni, &ne);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
cache: cache:
up_read(&nm_i->nat_tree_lock);
/* cache nat entry */ /* cache nat entry */
cache_nat_entry(NM_I(sbi), nid, &ne); down_write(&nm_i->nat_tree_lock);
cache_nat_entry(sbi, nid, &ne);
up_write(&nm_i->nat_tree_lock); up_write(&nm_i->nat_tree_lock);
} }
pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs)
{
const long direct_index = ADDRS_PER_INODE(dn->inode);
const long direct_blks = ADDRS_PER_BLOCK;
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
unsigned int skipped_unit = ADDRS_PER_BLOCK;
int cur_level = dn->cur_level;
int max_level = dn->max_level;
pgoff_t base = 0;
if (!dn->max_level)
return pgofs + 1;
while (max_level-- > cur_level)
skipped_unit *= NIDS_PER_BLOCK;
switch (dn->max_level) {
case 3:
base += 2 * indirect_blks;
case 2:
base += 2 * direct_blks;
case 1:
base += direct_index;
break;
default:
f2fs_bug_on(F2FS_I_SB(dn->inode), 1);
}
return ((pgofs - base) / skipped_unit + 1) * skipped_unit + base;
}
/* /*
* The maximum depth is four. * The maximum depth is four.
* Offset[0] will have raw inode offset. * Offset[0] will have raw inode offset.
*/ */
static int get_node_path(struct f2fs_inode_info *fi, long block, static int get_node_path(struct inode *inode, long block,
int offset[4], unsigned int noffset[4]) int offset[4], unsigned int noffset[4])
{ {
const long direct_index = ADDRS_PER_INODE(fi); const long direct_index = ADDRS_PER_INODE(inode);
const long direct_blks = ADDRS_PER_BLOCK; const long direct_blks = ADDRS_PER_BLOCK;
const long dptrs_per_blk = NIDS_PER_BLOCK; const long dptrs_per_blk = NIDS_PER_BLOCK;
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
@ -495,10 +530,10 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
int offset[4]; int offset[4];
unsigned int noffset[4]; unsigned int noffset[4];
nid_t nids[4]; nid_t nids[4];
int level, i; int level, i = 0;
int err = 0; int err = 0;
level = get_node_path(F2FS_I(dn->inode), index, offset, noffset); level = get_node_path(dn->inode, index, offset, noffset);
nids[0] = dn->inode->i_ino; nids[0] = dn->inode->i_ino;
npage[0] = dn->inode_page; npage[0] = dn->inode_page;
@ -585,6 +620,10 @@ release_pages:
release_out: release_out:
dn->inode_page = NULL; dn->inode_page = NULL;
dn->node_page = NULL; dn->node_page = NULL;
if (err == -ENOENT) {
dn->cur_level = i;
dn->max_level = level;
}
return err; return err;
} }
@ -792,7 +831,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from)
trace_f2fs_truncate_inode_blocks_enter(inode, from); trace_f2fs_truncate_inode_blocks_enter(inode, from);
level = get_node_path(F2FS_I(inode), from, offset, noffset); level = get_node_path(inode, from, offset, noffset);
restart: restart:
page = get_node_page(sbi, inode->i_ino); page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(page)) { if (IS_ERR(page)) {
@ -861,7 +900,7 @@ skip_partial:
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
goto restart; goto restart;
} }
f2fs_wait_on_page_writeback(page, NODE); f2fs_wait_on_page_writeback(page, NODE, true);
ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
set_page_dirty(page); set_page_dirty(page);
unlock_page(page); unlock_page(page);
@ -976,7 +1015,7 @@ struct page *new_node_page(struct dnode_of_data *dn,
new_ni.ino = dn->inode->i_ino; new_ni.ino = dn->inode->i_ino;
set_node_addr(sbi, &new_ni, NEW_ADDR, false); set_node_addr(sbi, &new_ni, NEW_ADDR, false);
f2fs_wait_on_page_writeback(page, NODE); f2fs_wait_on_page_writeback(page, NODE, true);
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true); fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
set_cold_node(dn->inode, page); set_cold_node(dn->inode, page);
SetPageUptodate(page); SetPageUptodate(page);
@ -1029,7 +1068,7 @@ static int read_node_page(struct page *page, int rw)
if (PageUptodate(page)) if (PageUptodate(page))
return LOCKED_PAGE; return LOCKED_PAGE;
fio.blk_addr = ni.blk_addr; fio.new_blkaddr = fio.old_blkaddr = ni.blk_addr;
return f2fs_submit_page_bio(&fio); return f2fs_submit_page_bio(&fio);
} }
@ -1045,12 +1084,11 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
return; return;
f2fs_bug_on(sbi, check_nid_range(sbi, nid)); f2fs_bug_on(sbi, check_nid_range(sbi, nid));
apage = find_get_page(NODE_MAPPING(sbi), nid); rcu_read_lock();
if (apage && PageUptodate(apage)) { apage = radix_tree_lookup(&NODE_MAPPING(sbi)->page_tree, nid);
f2fs_put_page(apage, 0); rcu_read_unlock();
if (apage)
return; return;
}
f2fs_put_page(apage, 0);
apage = grab_cache_page(NODE_MAPPING(sbi), nid); apage = grab_cache_page(NODE_MAPPING(sbi), nid);
if (!apage) if (!apage)
@ -1063,7 +1101,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
/* /*
* readahead MAX_RA_NODE number of node pages. * readahead MAX_RA_NODE number of node pages.
*/ */
void ra_node_pages(struct page *parent, int start) static void ra_node_pages(struct page *parent, int start)
{ {
struct f2fs_sb_info *sbi = F2FS_P_SB(parent); struct f2fs_sb_info *sbi = F2FS_P_SB(parent);
struct blk_plug plug; struct blk_plug plug;
@ -1083,7 +1121,7 @@ void ra_node_pages(struct page *parent, int start)
blk_finish_plug(&plug); blk_finish_plug(&plug);
} }
struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid, static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
struct page *parent, int start) struct page *parent, int start)
{ {
struct page *page; struct page *page;
@ -1154,19 +1192,57 @@ void sync_inode_page(struct dnode_of_data *dn)
dn->node_changed = ret ? true: false; dn->node_changed = ret ? true: false;
} }
static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
{
struct inode *inode;
struct page *page;
/* should flush inline_data before evict_inode */
inode = ilookup(sbi->sb, ino);
if (!inode)
return;
page = pagecache_get_page(inode->i_mapping, 0, FGP_NOWAIT, 0);
if (!page)
goto iput_out;
if (!trylock_page(page))
goto release_out;
if (!PageUptodate(page))
goto page_out;
if (!PageDirty(page))
goto page_out;
if (!clear_page_dirty_for_io(page))
goto page_out;
if (!f2fs_write_inline_data(inode, page))
inode_dec_dirty_pages(inode);
else
set_page_dirty(page);
page_out:
unlock_page(page);
release_out:
f2fs_put_page(page, 0);
iput_out:
iput(inode);
}
int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
pgoff_t index, end; pgoff_t index, end;
struct pagevec pvec; struct pagevec pvec;
int step = ino ? 2 : 0; int step = ino ? 2 : 0;
int nwritten = 0, wrote = 0; int nwritten = 0;
pagevec_init(&pvec, 0); pagevec_init(&pvec, 0);
next_step: next_step:
index = 0; index = 0;
end = LONG_MAX; end = ULONG_MAX;
while (index <= end) { while (index <= end) {
int i, nr_pages; int i, nr_pages;
@ -1203,6 +1279,7 @@ next_step:
* If an fsync mode, * If an fsync mode,
* we should not skip writing node pages. * we should not skip writing node pages.
*/ */
lock_node:
if (ino && ino_of_node(page) == ino) if (ino && ino_of_node(page) == ino)
lock_page(page); lock_page(page);
else if (!trylock_page(page)) else if (!trylock_page(page))
@ -1221,6 +1298,17 @@ continue_unlock:
goto continue_unlock; goto continue_unlock;
} }
/* flush inline_data */
if (!ino && is_inline_node(page)) {
clear_inline_node(page);
unlock_page(page);
flush_inline_data(sbi, ino_of_node(page));
goto lock_node;
}
f2fs_wait_on_page_writeback(page, NODE, true);
BUG_ON(PageWriteback(page));
if (!clear_page_dirty_for_io(page)) if (!clear_page_dirty_for_io(page))
goto continue_unlock; goto continue_unlock;
@ -1238,8 +1326,6 @@ continue_unlock:
if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc))
unlock_page(page); unlock_page(page);
else
wrote++;
if (--wbc->nr_to_write == 0) if (--wbc->nr_to_write == 0)
break; break;
@ -1257,15 +1343,12 @@ continue_unlock:
step++; step++;
goto next_step; goto next_step;
} }
if (wrote)
f2fs_submit_merged_bio(sbi, NODE, WRITE);
return nwritten; return nwritten;
} }
int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino) int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
{ {
pgoff_t index = 0, end = LONG_MAX; pgoff_t index = 0, end = ULONG_MAX;
struct pagevec pvec; struct pagevec pvec;
int ret2 = 0, ret = 0; int ret2 = 0, ret = 0;
@ -1287,7 +1370,7 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
continue; continue;
if (ino && ino_of_node(page) == ino) { if (ino && ino_of_node(page) == ino) {
f2fs_wait_on_page_writeback(page, NODE); f2fs_wait_on_page_writeback(page, NODE, true);
if (TestClearPageError(page)) if (TestClearPageError(page))
ret = -EIO; ret = -EIO;
} }
@ -1326,8 +1409,6 @@ static int f2fs_write_node_page(struct page *page,
if (unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
goto redirty_out; goto redirty_out;
f2fs_wait_on_page_writeback(page, NODE);
/* get old block addr of this node page */ /* get old block addr of this node page */
nid = nid_of_node(page); nid = nid_of_node(page);
f2fs_bug_on(sbi, page->index != nid); f2fs_bug_on(sbi, page->index != nid);
@ -1351,14 +1432,18 @@ static int f2fs_write_node_page(struct page *page,
} }
set_page_writeback(page); set_page_writeback(page);
fio.blk_addr = ni.blk_addr; fio.old_blkaddr = ni.blk_addr;
write_node_page(nid, &fio); write_node_page(nid, &fio);
set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page));
dec_page_count(sbi, F2FS_DIRTY_NODES); dec_page_count(sbi, F2FS_DIRTY_NODES);
up_read(&sbi->node_write); up_read(&sbi->node_write);
if (wbc->for_reclaim)
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE);
unlock_page(page); unlock_page(page);
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, NODE, WRITE); f2fs_submit_merged_bio(sbi, NODE, WRITE);
return 0; return 0;
@ -1374,8 +1459,6 @@ static int f2fs_write_node_pages(struct address_space *mapping,
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
long diff; long diff;
trace_f2fs_writepages(mapping->host, wbc, NODE);
/* balancing f2fs's metadata in background */ /* balancing f2fs's metadata in background */
f2fs_balance_fs_bg(sbi); f2fs_balance_fs_bg(sbi);
@ -1383,6 +1466,8 @@ static int f2fs_write_node_pages(struct address_space *mapping,
if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE)) if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE))
goto skip_write; goto skip_write;
trace_f2fs_writepages(mapping->host, wbc, NODE);
diff = nr_pages_to_write(sbi, NODE, wbc); diff = nr_pages_to_write(sbi, NODE, wbc);
wbc->sync_mode = WB_SYNC_NONE; wbc->sync_mode = WB_SYNC_NONE;
sync_node_pages(sbi, 0, wbc); sync_node_pages(sbi, 0, wbc);
@ -1391,6 +1476,7 @@ static int f2fs_write_node_pages(struct address_space *mapping,
skip_write: skip_write:
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES); wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES);
trace_f2fs_writepages(mapping->host, wbc, NODE);
return 0; return 0;
} }
@ -1526,7 +1612,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
int i = 0; int i = 0;
nid_t nid = nm_i->next_scan_nid; nid_t nid = nm_i->next_scan_nid;
@ -1558,16 +1644,18 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
nm_i->next_scan_nid = nid; nm_i->next_scan_nid = nid;
/* find free nids from current sum_pages */ /* find free nids from current sum_pages */
mutex_lock(&curseg->curseg_mutex); down_read(&curseg->journal_rwsem);
for (i = 0; i < nats_in_cursum(sum); i++) { for (i = 0; i < nats_in_cursum(journal); i++) {
block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr); block_t addr;
nid = le32_to_cpu(nid_in_journal(sum, i));
addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
nid = le32_to_cpu(nid_in_journal(journal, i));
if (addr == NULL_ADDR) if (addr == NULL_ADDR)
add_free_nid(sbi, nid, true); add_free_nid(sbi, nid, true);
else else
remove_free_nid(nm_i, nid); remove_free_nid(nm_i, nid);
} }
mutex_unlock(&curseg->curseg_mutex); up_read(&curseg->journal_rwsem);
up_read(&nm_i->nat_tree_lock); up_read(&nm_i->nat_tree_lock);
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid),
@ -1703,7 +1791,7 @@ void recover_inline_xattr(struct inode *inode, struct page *page)
src_addr = inline_xattr_addr(page); src_addr = inline_xattr_addr(page);
inline_size = inline_xattr_size(inode); inline_size = inline_xattr_size(inode);
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
memcpy(dst_addr, src_addr, inline_size); memcpy(dst_addr, src_addr, inline_size);
update_inode: update_inode:
update_inode(inode, ipage); update_inode(inode, ipage);
@ -1831,16 +1919,16 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
int i; int i;
mutex_lock(&curseg->curseg_mutex); down_write(&curseg->journal_rwsem);
for (i = 0; i < nats_in_cursum(sum); i++) { for (i = 0; i < nats_in_cursum(journal); i++) {
struct nat_entry *ne; struct nat_entry *ne;
struct f2fs_nat_entry raw_ne; struct f2fs_nat_entry raw_ne;
nid_t nid = le32_to_cpu(nid_in_journal(sum, i)); nid_t nid = le32_to_cpu(nid_in_journal(journal, i));
raw_ne = nat_in_journal(sum, i); raw_ne = nat_in_journal(journal, i);
ne = __lookup_nat_cache(nm_i, nid); ne = __lookup_nat_cache(nm_i, nid);
if (!ne) { if (!ne) {
@ -1849,8 +1937,8 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
} }
__set_nat_cache_dirty(nm_i, ne); __set_nat_cache_dirty(nm_i, ne);
} }
update_nats_in_cursum(sum, -i); update_nats_in_cursum(journal, -i);
mutex_unlock(&curseg->curseg_mutex); up_write(&curseg->journal_rwsem);
} }
static void __adjust_nat_entry_set(struct nat_entry_set *nes, static void __adjust_nat_entry_set(struct nat_entry_set *nes,
@ -1875,7 +1963,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
struct nat_entry_set *set) struct nat_entry_set *set)
{ {
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK; nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK;
bool to_journal = true; bool to_journal = true;
struct f2fs_nat_block *nat_blk; struct f2fs_nat_block *nat_blk;
@ -1887,11 +1975,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
* #1, flush nat entries to journal in current hot data summary block. * #1, flush nat entries to journal in current hot data summary block.
* #2, flush nat entries to nat page. * #2, flush nat entries to nat page.
*/ */
if (!__has_cursum_space(sum, set->entry_cnt, NAT_JOURNAL)) if (!__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL))
to_journal = false; to_journal = false;
if (to_journal) { if (to_journal) {
mutex_lock(&curseg->curseg_mutex); down_write(&curseg->journal_rwsem);
} else { } else {
page = get_next_nat_page(sbi, start_nid); page = get_next_nat_page(sbi, start_nid);
nat_blk = page_address(page); nat_blk = page_address(page);
@ -1908,11 +1996,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
continue; continue;
if (to_journal) { if (to_journal) {
offset = lookup_journal_in_cursum(sum, offset = lookup_journal_in_cursum(journal,
NAT_JOURNAL, nid, 1); NAT_JOURNAL, nid, 1);
f2fs_bug_on(sbi, offset < 0); f2fs_bug_on(sbi, offset < 0);
raw_ne = &nat_in_journal(sum, offset); raw_ne = &nat_in_journal(journal, offset);
nid_in_journal(sum, offset) = cpu_to_le32(nid); nid_in_journal(journal, offset) = cpu_to_le32(nid);
} else { } else {
raw_ne = &nat_blk->entries[nid - start_nid]; raw_ne = &nat_blk->entries[nid - start_nid];
} }
@ -1924,7 +2012,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
} }
if (to_journal) if (to_journal)
mutex_unlock(&curseg->curseg_mutex); up_write(&curseg->journal_rwsem);
else else
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
@ -1941,7 +2029,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
struct nat_entry_set *setvec[SETVEC_SIZE]; struct nat_entry_set *setvec[SETVEC_SIZE];
struct nat_entry_set *set, *tmp; struct nat_entry_set *set, *tmp;
unsigned int found; unsigned int found;
@ -1958,7 +2046,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
* entries, remove all entries from journal and merge them * entries, remove all entries from journal and merge them
* into nat entry set. * into nat entry set.
*/ */
if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL)) if (!__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL))
remove_nats_in_journal(sbi); remove_nats_in_journal(sbi);
while ((found = __gang_lookup_nat_set(nm_i, while ((found = __gang_lookup_nat_set(nm_i,
@ -1967,7 +2055,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
set_idx = setvec[found - 1]->set + 1; set_idx = setvec[found - 1]->set + 1;
for (idx = 0; idx < found; idx++) for (idx = 0; idx < found; idx++)
__adjust_nat_entry_set(setvec[idx], &sets, __adjust_nat_entry_set(setvec[idx], &sets,
MAX_NAT_JENTRIES(sum)); MAX_NAT_JENTRIES(journal));
} }
/* flush dirty nats in nat entry set */ /* flush dirty nats in nat entry set */
@ -2000,6 +2088,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
nm_i->nat_cnt = 0; nm_i->nat_cnt = 0;
nm_i->ram_thresh = DEF_RAM_THRESHOLD; nm_i->ram_thresh = DEF_RAM_THRESHOLD;
nm_i->ra_nid_pages = DEF_RA_NID_PAGES; nm_i->ra_nid_pages = DEF_RA_NID_PAGES;
nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD;
INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC);
INIT_LIST_HEAD(&nm_i->free_nid_list); INIT_LIST_HEAD(&nm_i->free_nid_list);

View File

@ -25,6 +25,9 @@
/* control the memory footprint threshold (10MB per 1GB ram) */ /* control the memory footprint threshold (10MB per 1GB ram) */
#define DEF_RAM_THRESHOLD 10 #define DEF_RAM_THRESHOLD 10
/* control dirty nats ratio threshold (default: 10% over max nid count) */
#define DEF_DIRTY_NAT_RATIO_THRESHOLD 10
/* vector size for gang look-up from nat cache that consists of radix tree */ /* vector size for gang look-up from nat cache that consists of radix tree */
#define NATVEC_SIZE 64 #define NATVEC_SIZE 64
#define SETVEC_SIZE 32 #define SETVEC_SIZE 32
@ -117,6 +120,12 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
raw_ne->version = ni->version; raw_ne->version = ni->version;
} }
static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi)
{
return NM_I(sbi)->dirty_nat_cnt >= NM_I(sbi)->max_nid *
NM_I(sbi)->dirty_nats_ratio / 100;
}
enum mem_type { enum mem_type {
FREE_NIDS, /* indicates the free nid list */ FREE_NIDS, /* indicates the free nid list */
NAT_ENTRIES, /* indicates the cached nat entry */ NAT_ENTRIES, /* indicates the cached nat entry */
@ -321,7 +330,7 @@ static inline int set_nid(struct page *p, int off, nid_t nid, bool i)
{ {
struct f2fs_node *rn = F2FS_NODE(p); struct f2fs_node *rn = F2FS_NODE(p);
f2fs_wait_on_page_writeback(p, NODE); f2fs_wait_on_page_writeback(p, NODE, true);
if (i) if (i)
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid); rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
@ -370,6 +379,21 @@ static inline int is_node(struct page *page, int type)
#define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT) #define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT)
#define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT) #define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT)
static inline int is_inline_node(struct page *page)
{
return PageChecked(page);
}
static inline void set_inline_node(struct page *page)
{
SetPageChecked(page);
}
static inline void clear_inline_node(struct page *page)
{
ClearPageChecked(page);
}
static inline void set_cold_node(struct inode *inode, struct page *page) static inline void set_cold_node(struct inode *inode, struct page *page)
{ {
struct f2fs_node *rn = F2FS_NODE(page); struct f2fs_node *rn = F2FS_NODE(page);

View File

@ -350,8 +350,7 @@ got_it:
inode = dn->inode; inode = dn->inode;
} }
bidx = start_bidx_of_node(offset, F2FS_I(inode)) + bidx = start_bidx_of_node(offset, inode) + le16_to_cpu(sum.ofs_in_node);
le16_to_cpu(sum.ofs_in_node);
/* /*
* if inode page is locked, unlock temporarily, but its reference * if inode page is locked, unlock temporarily, but its reference
@ -386,10 +385,9 @@ truncate_out:
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
struct page *page, block_t blkaddr) struct page *page, block_t blkaddr)
{ {
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int start, end;
struct dnode_of_data dn; struct dnode_of_data dn;
struct node_info ni; struct node_info ni;
unsigned int start, end;
int err = 0, recovered = 0; int err = 0, recovered = 0;
/* step 1: recover xattr */ /* step 1: recover xattr */
@ -409,8 +407,8 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
goto out; goto out;
/* step 3: recover data indices */ /* step 3: recover data indices */
start = start_bidx_of_node(ofs_of_node(page), fi); start = start_bidx_of_node(ofs_of_node(page), inode);
end = start + ADDRS_PER_PAGE(page, fi); end = start + ADDRS_PER_PAGE(page, inode);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
@ -418,7 +416,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
if (err) if (err)
goto out; goto out;
f2fs_wait_on_page_writeback(dn.node_page, NODE); f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
get_node_info(sbi, dn.nid, &ni); get_node_info(sbi, dn.nid, &ni);
f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
@ -467,7 +465,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
/* write dummy data page */ /* write dummy data page */
f2fs_replace_block(sbi, &dn, src, dest, f2fs_replace_block(sbi, &dn, src, dest,
ni.version, false); ni.version, false, false);
recovered++; recovered++;
} }
} }

View File

@ -191,70 +191,145 @@ void register_inmem_page(struct inode *inode, struct page *page)
trace_f2fs_register_inmem_page(page, INMEM); trace_f2fs_register_inmem_page(page, INMEM);
} }
int commit_inmem_pages(struct inode *inode, bool abort) static int __revoke_inmem_pages(struct inode *inode,
struct list_head *head, bool drop, bool recover)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct inmem_pages *cur, *tmp;
int err = 0;
list_for_each_entry_safe(cur, tmp, head, list) {
struct page *page = cur->page;
if (drop)
trace_f2fs_commit_inmem_page(page, INMEM_DROP);
lock_page(page);
if (recover) {
struct dnode_of_data dn;
struct node_info ni;
trace_f2fs_commit_inmem_page(page, INMEM_REVOKE);
set_new_dnode(&dn, inode, NULL, NULL, 0);
if (get_dnode_of_data(&dn, page->index, LOOKUP_NODE)) {
err = -EAGAIN;
goto next;
}
get_node_info(sbi, dn.nid, &ni);
f2fs_replace_block(sbi, &dn, dn.data_blkaddr,
cur->old_addr, ni.version, true, true);
f2fs_put_dnode(&dn);
}
next:
ClearPageUptodate(page);
set_page_private(page, 0);
ClearPageUptodate(page);
f2fs_put_page(page, 1);
list_del(&cur->list);
kmem_cache_free(inmem_entry_slab, cur);
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
}
return err;
}
void drop_inmem_pages(struct inode *inode)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
mutex_lock(&fi->inmem_lock);
__revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
mutex_unlock(&fi->inmem_lock);
}
static int __commit_inmem_pages(struct inode *inode,
struct list_head *revoke_list)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
struct inmem_pages *cur, *tmp; struct inmem_pages *cur, *tmp;
bool submit_bio = false;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
.sbi = sbi, .sbi = sbi,
.type = DATA, .type = DATA,
.rw = WRITE_SYNC | REQ_PRIO, .rw = WRITE_SYNC | REQ_PRIO,
.encrypted_page = NULL, .encrypted_page = NULL,
}; };
bool submit_bio = false;
int err = 0; int err = 0;
/* list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
* The abort is true only when f2fs_evict_inode is called. struct page *page = cur->page;
* Basically, the f2fs_evict_inode doesn't produce any data writes, so
* that we don't need to call f2fs_balance_fs. lock_page(page);
* Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this if (page->mapping == inode->i_mapping) {
* inode becomes free by iget_locked in f2fs_iget. trace_f2fs_commit_inmem_page(page, INMEM);
*/
if (!abort) { set_page_dirty(page);
f2fs_balance_fs(sbi, true); f2fs_wait_on_page_writeback(page, DATA, true);
f2fs_lock_op(sbi); if (clear_page_dirty_for_io(page))
inode_dec_dirty_pages(inode);
fio.page = page;
err = do_write_data_page(&fio);
if (err) {
unlock_page(page);
break;
}
/* record old blkaddr for revoking */
cur->old_addr = fio.old_blkaddr;
clear_cold_data(page);
submit_bio = true;
}
unlock_page(page);
list_move_tail(&cur->list, revoke_list);
} }
mutex_lock(&fi->inmem_lock); if (submit_bio)
list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
lock_page(cur->page);
if (!abort) {
if (cur->page->mapping == inode->i_mapping) {
set_page_dirty(cur->page);
f2fs_wait_on_page_writeback(cur->page, DATA);
if (clear_page_dirty_for_io(cur->page))
inode_dec_dirty_pages(inode);
trace_f2fs_commit_inmem_page(cur->page, INMEM);
fio.page = cur->page;
err = do_write_data_page(&fio);
if (err) {
unlock_page(cur->page);
break;
}
clear_cold_data(cur->page);
submit_bio = true;
}
} else {
ClearPageUptodate(cur->page);
trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP);
}
set_page_private(cur->page, 0);
ClearPagePrivate(cur->page);
f2fs_put_page(cur->page, 1);
list_del(&cur->list); if (!err)
kmem_cache_free(inmem_entry_slab, cur); __revoke_inmem_pages(inode, revoke_list, false, false);
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
return err;
}
int commit_inmem_pages(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct list_head revoke_list;
int err;
INIT_LIST_HEAD(&revoke_list);
f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
mutex_lock(&fi->inmem_lock);
err = __commit_inmem_pages(inode, &revoke_list);
if (err) {
int ret;
/*
* try to revoke all committed pages, but still we could fail
* due to no memory or other reason, if that happened, EAGAIN
* will be returned, which means in such case, transaction is
* already not integrity, caller should use journal to do the
* recovery or rewrite & commit last transaction. For other
* error number, revoking was done by filesystem itself.
*/
ret = __revoke_inmem_pages(inode, &revoke_list, false, true);
if (ret)
err = ret;
/* drop all uncommitted pages */
__revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
} }
mutex_unlock(&fi->inmem_lock); mutex_unlock(&fi->inmem_lock);
if (!abort) { f2fs_unlock_op(sbi);
f2fs_unlock_op(sbi);
if (submit_bio)
f2fs_submit_merged_bio(sbi, DATA, WRITE);
}
return err; return err;
} }
@ -291,11 +366,17 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
/* checkpoint is the only way to shrink partial cached entries */ /* checkpoint is the only way to shrink partial cached entries */
if (!available_free_memory(sbi, NAT_ENTRIES) || if (!available_free_memory(sbi, NAT_ENTRIES) ||
excess_prefree_segs(sbi) ||
!available_free_memory(sbi, INO_ENTRIES) || !available_free_memory(sbi, INO_ENTRIES) ||
excess_prefree_segs(sbi) ||
excess_dirty_nats(sbi) ||
(is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) {
if (test_opt(sbi, DATA_FLUSH)) if (test_opt(sbi, DATA_FLUSH)) {
struct blk_plug plug;
blk_start_plug(&plug);
sync_dirty_inodes(sbi, FILE_INODE); sync_dirty_inodes(sbi, FILE_INODE);
blk_finish_plug(&plug);
}
f2fs_sync_fs(sbi->sb, true); f2fs_sync_fs(sbi->sb, true);
stat_inc_bg_cp_count(sbi->stat_info); stat_inc_bg_cp_count(sbi->stat_info);
} }
@ -502,7 +583,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)
{ {
int err = -ENOTSUPP; int err = -EOPNOTSUPP;
if (test_opt(sbi, DISCARD)) { if (test_opt(sbi, DISCARD)) {
struct seg_entry *se = get_seg_entry(sbi, struct seg_entry *se = get_seg_entry(sbi,
@ -841,6 +922,31 @@ static void write_sum_page(struct f2fs_sb_info *sbi,
update_meta_page(sbi, (void *)sum_blk, blk_addr); update_meta_page(sbi, (void *)sum_blk, blk_addr);
} }
static void write_current_sum_page(struct f2fs_sb_info *sbi,
int type, block_t blk_addr)
{
struct curseg_info *curseg = CURSEG_I(sbi, type);
struct page *page = grab_meta_page(sbi, blk_addr);
struct f2fs_summary_block *src = curseg->sum_blk;
struct f2fs_summary_block *dst;
dst = (struct f2fs_summary_block *)page_address(page);
mutex_lock(&curseg->curseg_mutex);
down_read(&curseg->journal_rwsem);
memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE);
up_read(&curseg->journal_rwsem);
memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE);
memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE);
mutex_unlock(&curseg->curseg_mutex);
set_page_dirty(page);
f2fs_put_page(page, 1);
}
static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
{ {
struct curseg_info *curseg = CURSEG_I(sbi, type); struct curseg_info *curseg = CURSEG_I(sbi, type);
@ -873,9 +979,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) {
segno = find_next_zero_bit(free_i->free_segmap, segno = find_next_zero_bit(free_i->free_segmap,
MAIN_SEGS(sbi), *newseg + 1); (hint + 1) * sbi->segs_per_sec, *newseg + 1);
if (segno - *newseg < sbi->segs_per_sec - if (segno < (hint + 1) * sbi->segs_per_sec)
(*newseg % sbi->segs_per_sec))
goto got_it; goto got_it;
} }
find_other_zone: find_other_zone:
@ -1280,8 +1385,8 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
{ {
int type = __get_segment_type(fio->page, fio->type); int type = __get_segment_type(fio->page, fio->type);
allocate_data_block(fio->sbi, fio->page, fio->blk_addr, allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
&fio->blk_addr, sum, type); &fio->new_blkaddr, sum, type);
/* writeout dirty page into bdev */ /* writeout dirty page into bdev */
f2fs_submit_page_mbio(fio); f2fs_submit_page_mbio(fio);
@ -1293,7 +1398,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
.sbi = sbi, .sbi = sbi,
.type = META, .type = META,
.rw = WRITE_SYNC | REQ_META | REQ_PRIO, .rw = WRITE_SYNC | REQ_META | REQ_PRIO,
.blk_addr = page->index, .old_blkaddr = page->index,
.new_blkaddr = page->index,
.page = page, .page = page,
.encrypted_page = NULL, .encrypted_page = NULL,
}; };
@ -1323,19 +1429,19 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio)
get_node_info(sbi, dn->nid, &ni); get_node_info(sbi, dn->nid, &ni);
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
do_write_page(&sum, fio); do_write_page(&sum, fio);
dn->data_blkaddr = fio->blk_addr; f2fs_update_data_blkaddr(dn, fio->new_blkaddr);
} }
void rewrite_data_page(struct f2fs_io_info *fio) void rewrite_data_page(struct f2fs_io_info *fio)
{ {
fio->new_blkaddr = fio->old_blkaddr;
stat_inc_inplace_blocks(fio->sbi); stat_inc_inplace_blocks(fio->sbi);
f2fs_submit_page_mbio(fio); f2fs_submit_page_mbio(fio);
} }
static void __f2fs_replace_block(struct f2fs_sb_info *sbi, void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
struct f2fs_summary *sum,
block_t old_blkaddr, block_t new_blkaddr, block_t old_blkaddr, block_t new_blkaddr,
bool recover_curseg) bool recover_curseg, bool recover_newaddr)
{ {
struct sit_info *sit_i = SIT_I(sbi); struct sit_info *sit_i = SIT_I(sbi);
struct curseg_info *curseg; struct curseg_info *curseg;
@ -1378,7 +1484,7 @@ static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
__add_sum_entry(sbi, type, sum); __add_sum_entry(sbi, type, sum);
if (!recover_curseg) if (!recover_curseg || recover_newaddr)
update_sit_entry(sbi, new_blkaddr, 1); update_sit_entry(sbi, new_blkaddr, 1);
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
update_sit_entry(sbi, old_blkaddr, -1); update_sit_entry(sbi, old_blkaddr, -1);
@ -1402,66 +1508,30 @@ static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
block_t old_addr, block_t new_addr, block_t old_addr, block_t new_addr,
unsigned char version, bool recover_curseg) unsigned char version, bool recover_curseg,
bool recover_newaddr)
{ {
struct f2fs_summary sum; struct f2fs_summary sum;
set_summary(&sum, dn->nid, dn->ofs_in_node, version); set_summary(&sum, dn->nid, dn->ofs_in_node, version);
__f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg); __f2fs_replace_block(sbi, &sum, old_addr, new_addr,
recover_curseg, recover_newaddr);
dn->data_blkaddr = new_addr; f2fs_update_data_blkaddr(dn, new_addr);
set_data_blkaddr(dn);
f2fs_update_extent_cache(dn);
}
static inline bool is_merged_page(struct f2fs_sb_info *sbi,
struct page *page, enum page_type type)
{
enum page_type btype = PAGE_TYPE_OF_BIO(type);
struct f2fs_bio_info *io = &sbi->write_io[btype];
struct bio_vec *bvec;
struct page *target;
int i;
down_read(&io->io_rwsem);
if (!io->bio) {
up_read(&io->io_rwsem);
return false;
}
bio_for_each_segment_all(bvec, io->bio, i) {
if (bvec->bv_page->mapping) {
target = bvec->bv_page;
} else {
struct f2fs_crypto_ctx *ctx;
/* encrypted page */
ctx = (struct f2fs_crypto_ctx *)page_private(
bvec->bv_page);
target = ctx->w.control_page;
}
if (page == target) {
up_read(&io->io_rwsem);
return true;
}
}
up_read(&io->io_rwsem);
return false;
} }
void f2fs_wait_on_page_writeback(struct page *page, void f2fs_wait_on_page_writeback(struct page *page,
enum page_type type) enum page_type type, bool ordered)
{ {
if (PageWriteback(page)) { if (PageWriteback(page)) {
struct f2fs_sb_info *sbi = F2FS_P_SB(page); struct f2fs_sb_info *sbi = F2FS_P_SB(page);
if (is_merged_page(sbi, page, type)) f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE);
f2fs_submit_merged_bio(sbi, type, WRITE); if (ordered)
wait_on_page_writeback(page); wait_on_page_writeback(page);
else
wait_for_stable_page(page);
} }
} }
@ -1477,7 +1547,7 @@ void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi,
cpage = find_lock_page(META_MAPPING(sbi), blkaddr); cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
if (cpage) { if (cpage) {
f2fs_wait_on_page_writeback(cpage, DATA); f2fs_wait_on_page_writeback(cpage, DATA, true);
f2fs_put_page(cpage, 1); f2fs_put_page(cpage, 1);
} }
} }
@ -1498,12 +1568,11 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi)
/* Step 1: restore nat cache */ /* Step 1: restore nat cache */
seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
memcpy(&seg_i->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE); memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE);
/* Step 2: restore sit cache */ /* Step 2: restore sit cache */
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
memcpy(&seg_i->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE, memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE);
SUM_JOURNAL_SIZE);
offset = 2 * SUM_JOURNAL_SIZE; offset = 2 * SUM_JOURNAL_SIZE;
/* Step 3: restore summary entries */ /* Step 3: restore summary entries */
@ -1599,7 +1668,14 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
/* set uncompleted segment to curseg */ /* set uncompleted segment to curseg */
curseg = CURSEG_I(sbi, type); curseg = CURSEG_I(sbi, type);
mutex_lock(&curseg->curseg_mutex); mutex_lock(&curseg->curseg_mutex);
memcpy(curseg->sum_blk, sum, PAGE_CACHE_SIZE);
/* update journal info */
down_write(&curseg->journal_rwsem);
memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE);
up_write(&curseg->journal_rwsem);
memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE);
memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE);
curseg->next_segno = segno; curseg->next_segno = segno;
reset_curseg(sbi, type, 0); reset_curseg(sbi, type, 0);
curseg->alloc_type = ckpt->alloc_type[type]; curseg->alloc_type = ckpt->alloc_type[type];
@ -1654,13 +1730,12 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
/* Step 1: write nat cache */ /* Step 1: write nat cache */
seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
memcpy(kaddr, &seg_i->sum_blk->n_nats, SUM_JOURNAL_SIZE); memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE);
written_size += SUM_JOURNAL_SIZE; written_size += SUM_JOURNAL_SIZE;
/* Step 2: write sit cache */ /* Step 2: write sit cache */
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits, memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE);
SUM_JOURNAL_SIZE);
written_size += SUM_JOURNAL_SIZE; written_size += SUM_JOURNAL_SIZE;
/* Step 3: write summary entries */ /* Step 3: write summary entries */
@ -1706,12 +1781,8 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi,
else else
end = type + NR_CURSEG_NODE_TYPE; end = type + NR_CURSEG_NODE_TYPE;
for (i = type; i < end; i++) { for (i = type; i < end; i++)
struct curseg_info *sum = CURSEG_I(sbi, i); write_current_sum_page(sbi, i, blkaddr + (i - type));
mutex_lock(&sum->curseg_mutex);
write_sum_page(sbi, sum->sum_blk, blkaddr + (i - type));
mutex_unlock(&sum->curseg_mutex);
}
} }
void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
@ -1727,24 +1798,24 @@ void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE);
} }
int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, int lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
unsigned int val, int alloc) unsigned int val, int alloc)
{ {
int i; int i;
if (type == NAT_JOURNAL) { if (type == NAT_JOURNAL) {
for (i = 0; i < nats_in_cursum(sum); i++) { for (i = 0; i < nats_in_cursum(journal); i++) {
if (le32_to_cpu(nid_in_journal(sum, i)) == val) if (le32_to_cpu(nid_in_journal(journal, i)) == val)
return i; return i;
} }
if (alloc && __has_cursum_space(sum, 1, NAT_JOURNAL)) if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL))
return update_nats_in_cursum(sum, 1); return update_nats_in_cursum(journal, 1);
} else if (type == SIT_JOURNAL) { } else if (type == SIT_JOURNAL) {
for (i = 0; i < sits_in_cursum(sum); i++) for (i = 0; i < sits_in_cursum(journal); i++)
if (le32_to_cpu(segno_in_journal(sum, i)) == val) if (le32_to_cpu(segno_in_journal(journal, i)) == val)
return i; return i;
if (alloc && __has_cursum_space(sum, 1, SIT_JOURNAL)) if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL))
return update_sits_in_cursum(sum, 1); return update_sits_in_cursum(journal, 1);
} }
return -1; return -1;
} }
@ -1848,20 +1919,22 @@ static void add_sits_in_set(struct f2fs_sb_info *sbi)
static void remove_sits_in_journal(struct f2fs_sb_info *sbi) static void remove_sits_in_journal(struct f2fs_sb_info *sbi)
{ {
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
int i; int i;
for (i = sits_in_cursum(sum) - 1; i >= 0; i--) { down_write(&curseg->journal_rwsem);
for (i = 0; i < sits_in_cursum(journal); i++) {
unsigned int segno; unsigned int segno;
bool dirtied; bool dirtied;
segno = le32_to_cpu(segno_in_journal(sum, i)); segno = le32_to_cpu(segno_in_journal(journal, i));
dirtied = __mark_sit_entry_dirty(sbi, segno); dirtied = __mark_sit_entry_dirty(sbi, segno);
if (!dirtied) if (!dirtied)
add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); add_sit_entry(segno, &SM_I(sbi)->sit_entry_set);
} }
update_sits_in_cursum(sum, -sits_in_cursum(sum)); update_sits_in_cursum(journal, -i);
up_write(&curseg->journal_rwsem);
} }
/* /*
@ -1873,13 +1946,12 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
struct sit_info *sit_i = SIT_I(sbi); struct sit_info *sit_i = SIT_I(sbi);
unsigned long *bitmap = sit_i->dirty_sentries_bitmap; unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
struct sit_entry_set *ses, *tmp; struct sit_entry_set *ses, *tmp;
struct list_head *head = &SM_I(sbi)->sit_entry_set; struct list_head *head = &SM_I(sbi)->sit_entry_set;
bool to_journal = true; bool to_journal = true;
struct seg_entry *se; struct seg_entry *se;
mutex_lock(&curseg->curseg_mutex);
mutex_lock(&sit_i->sentry_lock); mutex_lock(&sit_i->sentry_lock);
if (!sit_i->dirty_sentries) if (!sit_i->dirty_sentries)
@ -1896,7 +1968,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
* entries, remove all entries from journal and add and account * entries, remove all entries from journal and add and account
* them in sit entry set. * them in sit entry set.
*/ */
if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL)) if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL))
remove_sits_in_journal(sbi); remove_sits_in_journal(sbi);
/* /*
@ -1913,10 +1985,12 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
unsigned int segno = start_segno; unsigned int segno = start_segno;
if (to_journal && if (to_journal &&
!__has_cursum_space(sum, ses->entry_cnt, SIT_JOURNAL)) !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL))
to_journal = false; to_journal = false;
if (!to_journal) { if (to_journal) {
down_write(&curseg->journal_rwsem);
} else {
page = get_next_sit_page(sbi, start_segno); page = get_next_sit_page(sbi, start_segno);
raw_sit = page_address(page); raw_sit = page_address(page);
} }
@ -1934,13 +2008,13 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
} }
if (to_journal) { if (to_journal) {
offset = lookup_journal_in_cursum(sum, offset = lookup_journal_in_cursum(journal,
SIT_JOURNAL, segno, 1); SIT_JOURNAL, segno, 1);
f2fs_bug_on(sbi, offset < 0); f2fs_bug_on(sbi, offset < 0);
segno_in_journal(sum, offset) = segno_in_journal(journal, offset) =
cpu_to_le32(segno); cpu_to_le32(segno);
seg_info_to_raw_sit(se, seg_info_to_raw_sit(se,
&sit_in_journal(sum, offset)); &sit_in_journal(journal, offset));
} else { } else {
sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
seg_info_to_raw_sit(se, seg_info_to_raw_sit(se,
@ -1952,7 +2026,9 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
ses->entry_cnt--; ses->entry_cnt--;
} }
if (!to_journal) if (to_journal)
up_write(&curseg->journal_rwsem);
else
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
f2fs_bug_on(sbi, ses->entry_cnt); f2fs_bug_on(sbi, ses->entry_cnt);
@ -1967,7 +2043,6 @@ out:
add_discard_addrs(sbi, cpc); add_discard_addrs(sbi, cpc);
} }
mutex_unlock(&sit_i->sentry_lock); mutex_unlock(&sit_i->sentry_lock);
mutex_unlock(&curseg->curseg_mutex);
set_prefree_as_free_segments(sbi); set_prefree_as_free_segments(sbi);
} }
@ -2099,6 +2174,11 @@ static int build_curseg(struct f2fs_sb_info *sbi)
array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL); array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (!array[i].sum_blk) if (!array[i].sum_blk)
return -ENOMEM; return -ENOMEM;
init_rwsem(&array[i].journal_rwsem);
array[i].journal = kzalloc(sizeof(struct f2fs_journal),
GFP_KERNEL);
if (!array[i].journal)
return -ENOMEM;
array[i].segno = NULL_SEGNO; array[i].segno = NULL_SEGNO;
array[i].next_blkoff = 0; array[i].next_blkoff = 0;
} }
@ -2109,11 +2189,11 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
{ {
struct sit_info *sit_i = SIT_I(sbi); struct sit_info *sit_i = SIT_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = curseg->journal;
int sit_blk_cnt = SIT_BLK_CNT(sbi); int sit_blk_cnt = SIT_BLK_CNT(sbi);
unsigned int i, start, end; unsigned int i, start, end;
unsigned int readed, start_blk = 0; unsigned int readed, start_blk = 0;
int nrpages = MAX_BIO_BLOCKS(sbi); int nrpages = MAX_BIO_BLOCKS(sbi) * 8;
do { do {
readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true); readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true);
@ -2127,16 +2207,16 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
struct f2fs_sit_entry sit; struct f2fs_sit_entry sit;
struct page *page; struct page *page;
mutex_lock(&curseg->curseg_mutex); down_read(&curseg->journal_rwsem);
for (i = 0; i < sits_in_cursum(sum); i++) { for (i = 0; i < sits_in_cursum(journal); i++) {
if (le32_to_cpu(segno_in_journal(sum, i)) if (le32_to_cpu(segno_in_journal(journal, i))
== start) { == start) {
sit = sit_in_journal(sum, i); sit = sit_in_journal(journal, i);
mutex_unlock(&curseg->curseg_mutex); up_read(&curseg->journal_rwsem);
goto got_it; goto got_it;
} }
} }
mutex_unlock(&curseg->curseg_mutex); up_read(&curseg->journal_rwsem);
page = get_current_sit_page(sbi, start); page = get_current_sit_page(sbi, start);
sit_blk = (struct f2fs_sit_block *)page_address(page); sit_blk = (struct f2fs_sit_block *)page_address(page);
@ -2371,8 +2451,10 @@ static void destroy_curseg(struct f2fs_sb_info *sbi)
if (!array) if (!array)
return; return;
SM_I(sbi)->curseg_array = NULL; SM_I(sbi)->curseg_array = NULL;
for (i = 0; i < NR_CURSEG_TYPE; i++) for (i = 0; i < NR_CURSEG_TYPE; i++) {
kfree(array[i].sum_blk); kfree(array[i].sum_blk);
kfree(array[i].journal);
}
kfree(array); kfree(array);
} }

View File

@ -183,7 +183,7 @@ struct segment_allocation {
* this value is set in page as a private data which indicate that * this value is set in page as a private data which indicate that
* the page is atomically written, and it is in inmem_pages list. * the page is atomically written, and it is in inmem_pages list.
*/ */
#define ATOMIC_WRITTEN_PAGE 0x0000ffff #define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
#define IS_ATOMIC_WRITTEN_PAGE(page) \ #define IS_ATOMIC_WRITTEN_PAGE(page) \
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE) (page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
@ -191,6 +191,7 @@ struct segment_allocation {
struct inmem_pages { struct inmem_pages {
struct list_head list; struct list_head list;
struct page *page; struct page *page;
block_t old_addr; /* for revoking when fail to commit */
}; };
struct sit_info { struct sit_info {
@ -257,6 +258,8 @@ struct victim_selection {
struct curseg_info { struct curseg_info {
struct mutex curseg_mutex; /* lock for consistency */ struct mutex curseg_mutex; /* lock for consistency */
struct f2fs_summary_block *sum_blk; /* cached summary block */ struct f2fs_summary_block *sum_blk; /* cached summary block */
struct rw_semaphore journal_rwsem; /* protect journal area */
struct f2fs_journal *journal; /* cached journal info */
unsigned char alloc_type; /* current allocation type */ unsigned char alloc_type; /* current allocation type */
unsigned int segno; /* current segment number */ unsigned int segno; /* current segment number */
unsigned short next_blkoff; /* next block offset to write */ unsigned short next_blkoff; /* next block offset to write */

View File

@ -126,6 +126,19 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
return NULL; 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 f2fs_sbi_show(struct f2fs_attr *a, static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
struct f2fs_sb_info *sbi, char *buf) struct f2fs_sb_info *sbi, char *buf)
{ {
@ -204,6 +217,9 @@ static struct f2fs_attr f2fs_attr_##_name = { \
f2fs_sbi_show, f2fs_sbi_store, \ f2fs_sbi_show, f2fs_sbi_store, \
offsetof(struct struct_name, elname)) offsetof(struct struct_name, elname))
#define F2FS_GENERAL_RO_ATTR(name) \
static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_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_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_no_gc_sleep_time, no_gc_sleep_time);
@ -216,10 +232,12 @@ 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_fsync_blocks, min_fsync_blocks);
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); 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, 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, 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, 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, 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, idle_interval, interval_time[REQ_TIME]);
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
#define ATTR_LIST(name) (&f2fs_attr_##name.attr) #define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = { static struct attribute *f2fs_attrs[] = {
@ -237,8 +255,10 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(dir_level), ATTR_LIST(dir_level),
ATTR_LIST(ram_thresh), ATTR_LIST(ram_thresh),
ATTR_LIST(ra_nid_pages), ATTR_LIST(ra_nid_pages),
ATTR_LIST(dirty_nats_ratio),
ATTR_LIST(cp_interval), ATTR_LIST(cp_interval),
ATTR_LIST(idle_interval), ATTR_LIST(idle_interval),
ATTR_LIST(lifetime_write_kbytes),
NULL, NULL,
}; };
@ -450,10 +470,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
/* Will be used by directory only */ /* Will be used by directory only */
fi->i_dir_level = F2FS_SB(sb)->dir_level; fi->i_dir_level = F2FS_SB(sb)->dir_level;
#ifdef CONFIG_F2FS_FS_ENCRYPTION
fi->i_crypt_info = NULL;
#endif
return &fi->vfs_inode; return &fi->vfs_inode;
} }
@ -474,7 +490,7 @@ static int f2fs_drop_inode(struct inode *inode)
/* some remained atomic pages should discarded */ /* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode)) if (f2fs_is_atomic_file(inode))
commit_inmem_pages(inode, true); drop_inmem_pages(inode);
/* should remain fi->extent_tree for writepage */ /* should remain fi->extent_tree for writepage */
f2fs_destroy_extent_node(inode); f2fs_destroy_extent_node(inode);
@ -487,11 +503,7 @@ static int f2fs_drop_inode(struct inode *inode)
sb_end_intwrite(inode->i_sb); sb_end_intwrite(inode->i_sb);
#ifdef CONFIG_F2FS_FS_ENCRYPTION fscrypt_put_encryption_info(inode, NULL);
if (F2FS_I(inode)->i_crypt_info)
f2fs_free_encryption_info(inode,
F2FS_I(inode)->i_crypt_info);
#endif
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
atomic_dec(&inode->i_count); atomic_dec(&inode->i_count);
} }
@ -562,6 +574,10 @@ static void f2fs_put_super(struct super_block *sb)
f2fs_leave_shrinker(sbi); f2fs_leave_shrinker(sbi);
mutex_unlock(&sbi->umount_mutex); mutex_unlock(&sbi->umount_mutex);
/* our cp_error case, we can wait for any writeback page */
if (get_pages(sbi, F2FS_WRITEBACK))
f2fs_flush_merged_bios(sbi);
iput(sbi->node_inode); iput(sbi->node_inode);
iput(sbi->meta_inode); iput(sbi->meta_inode);
@ -574,6 +590,8 @@ static void f2fs_put_super(struct super_block *sb)
wait_for_completion(&sbi->s_kobj_unregister); wait_for_completion(&sbi->s_kobj_unregister);
sb->s_fs_info = NULL; sb->s_fs_info = NULL;
if (sbi->s_chksum_driver)
crypto_free_shash(sbi->s_chksum_driver);
kfree(sbi->raw_super); kfree(sbi->raw_super);
kfree(sbi); kfree(sbi);
} }
@ -766,8 +784,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
bool need_stop_gc = false; bool need_stop_gc = false;
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE); bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
sync_filesystem(sb);
/* /*
* Save the old mount options in case we * Save the old mount options in case we
* need to restore them. * need to restore them.
@ -775,6 +791,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
org_mount_opt = sbi->mount_opt; org_mount_opt = sbi->mount_opt;
active_logs = sbi->active_logs; active_logs = sbi->active_logs;
if (*flags & MS_RDONLY) {
set_opt(sbi, FASTBOOT);
set_sbi_flag(sbi, SBI_IS_DIRTY);
}
sync_filesystem(sb);
sbi->mount_opt.opt = 0; sbi->mount_opt.opt = 0;
default_options(sbi); default_options(sbi);
@ -862,6 +885,41 @@ static struct super_operations f2fs_sops = {
.remount_fs = f2fs_remount, .remount_fs = f2fs_remount,
}; };
#ifdef CONFIG_F2FS_FS_ENCRYPTION
static int f2fs_get_context(struct inode *inode, void *ctx, size_t len)
{
return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
ctx, len, NULL);
}
static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
void *fs_data)
{
return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
ctx, len, fs_data, XATTR_CREATE);
}
static unsigned f2fs_max_namelen(struct inode *inode)
{
return S_ISLNK(inode->i_mode) ?
inode->i_sb->s_blocksize : F2FS_NAME_LEN;
}
static struct fscrypt_operations f2fs_cryptops = {
.get_context = f2fs_get_context,
.set_context = f2fs_set_context,
.is_encrypted = f2fs_encrypted_inode,
.empty_dir = f2fs_empty_dir,
.max_namelen = f2fs_max_namelen,
};
#else
static struct fscrypt_operations f2fs_cryptops = {
.is_encrypted = f2fs_encrypted_inode,
};
#endif
static struct inode *f2fs_nfs_get_inode(struct super_block *sb, static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
u64 ino, u32 generation) u64 ino, u32 generation)
{ {
@ -1074,7 +1132,7 @@ static int sanity_check_raw_super(struct super_block *sb,
return 0; return 0;
} }
static int sanity_check_ckpt(struct f2fs_sb_info *sbi) int sanity_check_ckpt(struct f2fs_sb_info *sbi)
{ {
unsigned int total, fsmeta; unsigned int total, fsmeta;
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
@ -1134,14 +1192,15 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
/* /*
* Read f2fs raw super block. * Read f2fs raw super block.
* Because we have two copies of super block, so read the first one at first, * Because we have two copies of super block, so read both of them
* if the first one is invalid, move to read the second one. * to get the first valid one. If any one of them is broken, we pass
* them recovery flag back to the caller.
*/ */
static int read_raw_super_block(struct super_block *sb, static int read_raw_super_block(struct super_block *sb,
struct f2fs_super_block **raw_super, struct f2fs_super_block **raw_super,
int *valid_super_block, int *recovery) int *valid_super_block, int *recovery)
{ {
int block = 0; int block;
struct buffer_head *bh; struct buffer_head *bh;
struct f2fs_super_block *super, *buf; struct f2fs_super_block *super, *buf;
int err = 0; int err = 0;
@ -1149,50 +1208,48 @@ static int read_raw_super_block(struct super_block *sb,
super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL); super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL);
if (!super) if (!super)
return -ENOMEM; return -ENOMEM;
retry:
bh = sb_bread(sb, block); for (block = 0; block < 2; block++) {
if (!bh) { bh = sb_bread(sb, block);
*recovery = 1; if (!bh) {
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
block + 1); block + 1);
err = -EIO; err = -EIO;
goto next; continue;
} }
buf = (struct f2fs_super_block *)(bh->b_data + F2FS_SUPER_OFFSET); buf = (struct f2fs_super_block *)
(bh->b_data + F2FS_SUPER_OFFSET);
/* sanity checking of raw super */ /* sanity checking of raw super */
if (sanity_check_raw_super(sb, buf)) { if (sanity_check_raw_super(sb, buf)) {
f2fs_msg(sb, KERN_ERR,
"Can't find valid F2FS filesystem in %dth superblock",
block + 1);
err = -EINVAL;
brelse(bh);
continue;
}
if (!*raw_super) {
memcpy(super, buf, sizeof(*super));
*valid_super_block = block;
*raw_super = super;
}
brelse(bh); brelse(bh);
}
/* Fail to read any one of the superblocks*/
if (err < 0)
*recovery = 1; *recovery = 1;
f2fs_msg(sb, KERN_ERR,
"Can't find valid F2FS filesystem in %dth superblock",
block + 1);
err = -EINVAL;
goto next;
}
if (!*raw_super) {
memcpy(super, buf, sizeof(*super));
*valid_super_block = block;
*raw_super = super;
}
brelse(bh);
next:
/* check the validity of the second superblock */
if (block == 0) {
block++;
goto retry;
}
/* No valid superblock */ /* No valid superblock */
if (!*raw_super) { if (!*raw_super)
kfree(super); kfree(super);
return err; else
} err = 0;
return 0; return err;
} }
static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block) static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block)
@ -1242,6 +1299,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
bool retry = true, need_fsck = false; bool retry = true, need_fsck = false;
char *options = NULL; char *options = NULL;
int recovery, i, valid_super_block; int recovery, i, valid_super_block;
struct curseg_info *seg_i;
try_onemore: try_onemore:
err = -EINVAL; err = -EINVAL;
@ -1254,6 +1312,15 @@ try_onemore:
if (!sbi) if (!sbi)
return -ENOMEM; return -ENOMEM;
/* Load the checksum driver */
sbi->s_chksum_driver = crypto_alloc_shash("crc32", 0, 0);
if (IS_ERR(sbi->s_chksum_driver)) {
f2fs_msg(sb, KERN_ERR, "Cannot load crc32 driver.");
err = PTR_ERR(sbi->s_chksum_driver);
sbi->s_chksum_driver = NULL;
goto free_sbi;
}
/* set a block size */ /* set a block size */
if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) { if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) {
f2fs_msg(sb, KERN_ERR, "unable to set blocksize"); f2fs_msg(sb, KERN_ERR, "unable to set blocksize");
@ -1285,6 +1352,7 @@ try_onemore:
get_random_bytes(&sbi->s_next_generation, sizeof(u32)); get_random_bytes(&sbi->s_next_generation, sizeof(u32));
sb->s_op = &f2fs_sops; sb->s_op = &f2fs_sops;
sb->s_cop = &f2fs_cryptops;
sb->s_xattr = f2fs_xattr_handlers; sb->s_xattr = f2fs_xattr_handlers;
sb->s_export_op = &f2fs_export_ops; sb->s_export_op = &f2fs_export_ops;
sb->s_magic = F2FS_SUPER_MAGIC; sb->s_magic = F2FS_SUPER_MAGIC;
@ -1333,13 +1401,6 @@ try_onemore:
goto free_meta_inode; goto free_meta_inode;
} }
/* sanity checking of checkpoint */
err = -EINVAL;
if (sanity_check_ckpt(sbi)) {
f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint");
goto free_cp;
}
sbi->total_valid_node_count = sbi->total_valid_node_count =
le32_to_cpu(sbi->ckpt->valid_node_count); le32_to_cpu(sbi->ckpt->valid_node_count);
sbi->total_valid_inode_count = sbi->total_valid_inode_count =
@ -1372,6 +1433,17 @@ try_onemore:
goto free_nm; goto free_nm;
} }
/* For write statistics */
if (sb->s_bdev->bd_part)
sbi->sectors_written_start =
(u64)part_stat_read(sb->s_bdev->bd_part, sectors[1]);
/* Read accumulated write IO statistics if exists */
seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
if (__exist_node_summaries(sbi))
sbi->kbytes_written =
le64_to_cpu(seg_i->sum_blk->journal.info.kbytes_written);
build_gc_manager(sbi); build_gc_manager(sbi);
/* get an inode for node space */ /* get an inode for node space */
@ -1466,8 +1538,10 @@ try_onemore:
/* recover broken superblock */ /* recover broken superblock */
if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) { if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) {
f2fs_msg(sb, KERN_INFO, "Recover invalid superblock"); err = f2fs_commit_super(sbi, true);
f2fs_commit_super(sbi, true); f2fs_msg(sb, KERN_INFO,
"Try to recover %dth superblock, ret: %ld",
sbi->valid_super_block ? 1 : 2, err);
} }
f2fs_update_time(sbi, CP_TIME); f2fs_update_time(sbi, CP_TIME);
@ -1496,7 +1570,6 @@ free_nm:
destroy_node_manager(sbi); destroy_node_manager(sbi);
free_sm: free_sm:
destroy_segment_manager(sbi); destroy_segment_manager(sbi);
free_cp:
kfree(sbi->ckpt); kfree(sbi->ckpt);
free_meta_inode: free_meta_inode:
make_bad_inode(sbi->meta_inode); make_bad_inode(sbi->meta_inode);
@ -1506,6 +1579,8 @@ free_options:
free_sb_buf: free_sb_buf:
kfree(raw_super); kfree(raw_super);
free_sbi: free_sbi:
if (sbi->s_chksum_driver)
crypto_free_shash(sbi->s_chksum_driver);
kfree(sbi); kfree(sbi);
/* give only one another chance */ /* give only one another chance */
@ -1585,13 +1660,9 @@ static int __init init_f2fs_fs(void)
err = -ENOMEM; err = -ENOMEM;
goto free_extent_cache; goto free_extent_cache;
} }
err = f2fs_init_crypto();
if (err)
goto free_kset;
err = register_shrinker(&f2fs_shrinker_info); err = register_shrinker(&f2fs_shrinker_info);
if (err) if (err)
goto free_crypto; goto free_kset;
err = register_filesystem(&f2fs_fs_type); err = register_filesystem(&f2fs_fs_type);
if (err) if (err)
@ -1606,8 +1677,6 @@ free_filesystem:
unregister_filesystem(&f2fs_fs_type); unregister_filesystem(&f2fs_fs_type);
free_shrinker: free_shrinker:
unregister_shrinker(&f2fs_shrinker_info); unregister_shrinker(&f2fs_shrinker_info);
free_crypto:
f2fs_exit_crypto();
free_kset: free_kset:
kset_unregister(f2fs_kset); kset_unregister(f2fs_kset);
free_extent_cache: free_extent_cache:
@ -1630,7 +1699,6 @@ static void __exit exit_f2fs_fs(void)
f2fs_destroy_root_stats(); f2fs_destroy_root_stats();
unregister_shrinker(&f2fs_shrinker_info); unregister_shrinker(&f2fs_shrinker_info);
unregister_filesystem(&f2fs_fs_type); unregister_filesystem(&f2fs_fs_type);
f2fs_exit_crypto();
destroy_extent_cache(); destroy_extent_cache();
destroy_checkpoint_caches(); destroy_checkpoint_caches();
destroy_segment_manager_caches(); destroy_segment_manager_caches();

View File

@ -29,7 +29,8 @@ static inline void __print_last_io(void)
last_io.major, last_io.minor, last_io.major, last_io.minor,
last_io.pid, "----------------", last_io.pid, "----------------",
last_io.type, last_io.type,
last_io.fio.rw, last_io.fio.blk_addr, last_io.fio.rw,
last_io.fio.new_blkaddr,
last_io.len); last_io.len);
memset(&last_io, 0, sizeof(last_io)); memset(&last_io, 0, sizeof(last_io));
} }
@ -101,7 +102,8 @@ void f2fs_trace_ios(struct f2fs_io_info *fio, int flush)
last_io.pid == pid && last_io.pid == pid &&
last_io.type == __file_type(inode, pid) && last_io.type == __file_type(inode, pid) &&
last_io.fio.rw == fio->rw && last_io.fio.rw == fio->rw &&
last_io.fio.blk_addr + last_io.len == fio->blk_addr) { last_io.fio.new_blkaddr + last_io.len ==
fio->new_blkaddr) {
last_io.len++; last_io.len++;
return; return;
} }

View File

@ -300,7 +300,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
if (ipage) { if (ipage) {
inline_addr = inline_xattr_addr(ipage); inline_addr = inline_xattr_addr(ipage);
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE, true);
} else { } else {
page = get_node_page(sbi, inode->i_ino); page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(page)) { if (IS_ERR(page)) {
@ -308,7 +308,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
return PTR_ERR(page); return PTR_ERR(page);
} }
inline_addr = inline_xattr_addr(page); inline_addr = inline_xattr_addr(page);
f2fs_wait_on_page_writeback(page, NODE); f2fs_wait_on_page_writeback(page, NODE, true);
} }
memcpy(inline_addr, txattr_addr, inline_size); memcpy(inline_addr, txattr_addr, inline_size);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
@ -329,7 +329,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
return PTR_ERR(xpage); return PTR_ERR(xpage);
} }
f2fs_bug_on(sbi, new_nid); f2fs_bug_on(sbi, new_nid);
f2fs_wait_on_page_writeback(xpage, NODE); f2fs_wait_on_page_writeback(xpage, NODE, true);
} else { } else {
struct dnode_of_data dn; struct dnode_of_data dn;
set_new_dnode(&dn, inode, NULL, NULL, new_nid); set_new_dnode(&dn, inode, NULL, NULL, new_nid);

View File

@ -126,7 +126,8 @@ extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
#define f2fs_xattr_handlers NULL #define f2fs_xattr_handlers NULL
static inline int f2fs_setxattr(struct inode *inode, int index, static inline int f2fs_setxattr(struct inode *inode, int index,
const char *name, const void *value, size_t size, int flags) const char *name, const void *value, size_t size,
struct page *page, int flags)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -228,6 +228,8 @@ struct dentry_operations {
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */ #define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
#define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */ #define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */
#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */
extern seqlock_t rename_lock; extern seqlock_t rename_lock;
/* /*

View File

@ -21,7 +21,7 @@
#define F2FS_BLKSIZE 4096 /* support only 4KB block */ #define F2FS_BLKSIZE 4096 /* support only 4KB block */
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */ #define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) #define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS)
#define NULL_ADDR ((block_t)0) /* used as block_t addresses */ #define NULL_ADDR ((block_t)0) /* used as block_t addresses */
#define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */
@ -170,12 +170,12 @@ struct f2fs_extent {
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ #define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */
#define ADDRS_PER_INODE(fi) addrs_per_inode(fi) #define ADDRS_PER_INODE(inode) addrs_per_inode(inode)
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
#define ADDRS_PER_PAGE(page, fi) \ #define ADDRS_PER_PAGE(page, inode) \
(IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK) (IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK)
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
@ -345,7 +345,7 @@ struct f2fs_summary {
struct summary_footer { struct summary_footer {
unsigned char entry_type; /* SUM_TYPE_XXX */ unsigned char entry_type; /* SUM_TYPE_XXX */
__u32 check_sum; /* summary checksum */ __le32 check_sum; /* summary checksum */
} __packed; } __packed;
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\ #define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
@ -358,6 +358,12 @@ struct summary_footer {
sizeof(struct sit_journal_entry)) sizeof(struct sit_journal_entry))
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ #define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
sizeof(struct sit_journal_entry)) sizeof(struct sit_journal_entry))
/* Reserved area should make size of f2fs_extra_info equals to
* that of nat_journal and sit_journal.
*/
#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8)
/* /*
* frequently updated NAT/SIT entries can be stored in the spare area in * frequently updated NAT/SIT entries can be stored in the spare area in
* summary blocks * summary blocks
@ -387,18 +393,28 @@ struct sit_journal {
__u8 reserved[SIT_JOURNAL_RESERVED]; __u8 reserved[SIT_JOURNAL_RESERVED];
} __packed; } __packed;
/* 4KB-sized summary block structure */ struct f2fs_extra_info {
struct f2fs_summary_block { __le64 kbytes_written;
struct f2fs_summary entries[ENTRIES_IN_SUM]; __u8 reserved[EXTRA_INFO_RESERVED];
} __packed;
struct f2fs_journal {
union { union {
__le16 n_nats; __le16 n_nats;
__le16 n_sits; __le16 n_sits;
}; };
/* spare area is used by NAT or SIT journals */ /* spare area is used by NAT or SIT journals or extra info */
union { union {
struct nat_journal nat_j; struct nat_journal nat_j;
struct sit_journal sit_j; struct sit_journal sit_j;
struct f2fs_extra_info info;
}; };
} __packed;
/* 4KB-sized summary block structure */
struct f2fs_summary_block {
struct f2fs_summary entries[ENTRIES_IN_SUM];
struct f2fs_journal journal;
struct summary_footer footer; struct summary_footer footer;
} __packed; } __packed;

View File

@ -53,6 +53,8 @@ struct swap_info_struct;
struct seq_file; struct seq_file;
struct workqueue_struct; struct workqueue_struct;
struct iov_iter; struct iov_iter;
struct fscrypt_info;
struct fscrypt_operations;
extern void __init inode_init(void); extern void __init inode_init(void);
extern void __init inode_init_early(void); extern void __init inode_init_early(void);
@ -679,6 +681,10 @@ struct inode {
struct hlist_head i_fsnotify_marks; struct hlist_head i_fsnotify_marks;
#endif #endif
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
struct fscrypt_info *i_crypt_info;
#endif
void *i_private; /* fs or device private pointer */ void *i_private; /* fs or device private pointer */
}; };
@ -1324,6 +1330,8 @@ struct super_block {
#endif #endif
const struct xattr_handler **s_xattr; const struct xattr_handler **s_xattr;
const struct fscrypt_operations *s_cop;
struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */ struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_mounts; /* list of mounts; _not_ for fs use */ struct list_head s_mounts; /* list of mounts; _not_ for fs use */
struct block_device *s_bdev; struct block_device *s_bdev;

434
include/linux/fscrypto.h Normal file
View File

@ -0,0 +1,434 @@
/*
* 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;
struct key *ci_keyring_key;
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 }
static inline int fscrypt_key_size(int mode)
{
switch (mode) {
case FS_ENCRYPTION_MODE_AES_256_XTS:
return FS_AES_256_XTS_KEY_SIZE;
case FS_ENCRYPTION_MODE_AES_256_GCM:
return FS_AES_256_GCM_KEY_SIZE;
case FS_ENCRYPTION_MODE_AES_256_CBC:
return FS_AES_256_CBC_KEY_SIZE;
case FS_ENCRYPTION_MODE_AES_256_CTS:
return FS_AES_256_CTS_KEY_SIZE;
default:
BUG();
}
return 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 (*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 u32 fscrypt_validate_encryption_key_size(u32 mode, u32 size)
{
if (size == fscrypt_key_size(mode))
return size;
return 0;
}
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 *);
extern void fscrypt_release_ctx(struct fscrypt_ctx *);
extern struct page *fscrypt_encrypt_page(struct inode *, struct page *);
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 inode *,
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 get_crypt_info(struct inode *);
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)
{
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)
{
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 inode *i,
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 */

View File

@ -52,6 +52,7 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
{ META_FLUSH, "META_FLUSH" }, \ { META_FLUSH, "META_FLUSH" }, \
{ INMEM, "INMEM" }, \ { INMEM, "INMEM" }, \
{ INMEM_DROP, "INMEM_DROP" }, \ { INMEM_DROP, "INMEM_DROP" }, \
{ INMEM_REVOKE, "INMEM_REVOKE" }, \
{ IPU, "IN-PLACE" }, \ { IPU, "IN-PLACE" }, \
{ OPU, "OUT-OF-PLACE" }) { OPU, "OUT-OF-PLACE" })
@ -727,7 +728,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
__field(dev_t, dev) __field(dev_t, dev)
__field(ino_t, ino) __field(ino_t, ino)
__field(pgoff_t, index) __field(pgoff_t, index)
__field(block_t, blkaddr) __field(block_t, old_blkaddr)
__field(block_t, new_blkaddr)
__field(int, rw) __field(int, rw)
__field(int, type) __field(int, type)
), ),
@ -736,16 +738,18 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
__entry->dev = page->mapping->host->i_sb->s_dev; __entry->dev = page->mapping->host->i_sb->s_dev;
__entry->ino = page->mapping->host->i_ino; __entry->ino = page->mapping->host->i_ino;
__entry->index = page->index; __entry->index = page->index;
__entry->blkaddr = fio->blk_addr; __entry->old_blkaddr = fio->old_blkaddr;
__entry->new_blkaddr = fio->new_blkaddr;
__entry->rw = fio->rw; __entry->rw = fio->rw;
__entry->type = fio->type; __entry->type = fio->type;
), ),
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, " TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
"blkaddr = 0x%llx, rw = %s%s, type = %s", "oldaddr = 0x%llx, newaddr = 0x%llx rw = %s%s, type = %s",
show_dev_ino(__entry), show_dev_ino(__entry),
(unsigned long)__entry->index, (unsigned long)__entry->index,
(unsigned long long)__entry->blkaddr, (unsigned long long)__entry->old_blkaddr,
(unsigned long long)__entry->new_blkaddr,
show_bio_type(__entry->rw), show_bio_type(__entry->rw),
show_block_type(__entry->type)) show_block_type(__entry->type))
); );

View File

@ -246,6 +246,24 @@ struct fsxattr {
#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) #define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
#define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) #define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr)
/*
* File system encryption support
*/
/* Policy provided via an ioctl on the topmost directory */
#define FS_KEY_DESCRIPTOR_SIZE 8
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)
/* /*
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
* *