Compare commits
16 commits
Author | SHA1 | Date | |
---|---|---|---|
|
2ca42b3266 | ||
|
f1aab8f9b8 | ||
|
e5d590d279 | ||
|
4c3ff3cd9b | ||
|
57cc77b401 | ||
|
ea856bf788 | ||
|
7887be4d99 | ||
|
4247f93e60 | ||
|
b7554209bf | ||
|
22449473d7 | ||
|
6a9bd2721f | ||
|
59334583dc | ||
|
9019c15774 | ||
|
3c3684dec4 | ||
|
2be4c680cb | ||
|
3cb9384a2f |
61 changed files with 18389 additions and 3681 deletions
|
@ -74,3 +74,41 @@ Date: March 2014
|
|||
Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
|
||||
Description:
|
||||
Controls the memory footprint used by f2fs.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/trim_sections
|
||||
Date: February 2015
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls the trimming rate in batch mode.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/cp_interval
|
||||
Date: October 2015
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls the checkpoint timing.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/idle_interval
|
||||
Date: January 2016
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls the idle timing.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/ra_nid_pages
|
||||
Date: October 2015
|
||||
Contact: "Chao Yu" <chao2.yu@samsung.com>
|
||||
Description:
|
||||
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.
|
||||
|
|
|
@ -106,7 +106,11 @@ background_gc=%s Turn on/off cleaning operations, namely garbage
|
|||
Default value for this option is on. So garbage
|
||||
collection is on by default.
|
||||
disable_roll_forward Disable the roll-forward recovery routine
|
||||
discard Issue discard/TRIM commands when a segment is cleaned.
|
||||
norecovery Disable the roll-forward recovery routine, mounted read-
|
||||
only (i.e., -o ro,disable_roll_forward)
|
||||
discard/nodiscard Enable/disable real-time discard in f2fs, if discard is
|
||||
enabled, f2fs will issue discard/TRIM commands when a
|
||||
segment is cleaned.
|
||||
no_heap Disable heap-style segment allocation which finds free
|
||||
segments for data from the beginning of main area, while
|
||||
for node from the end of main area.
|
||||
|
@ -131,6 +135,22 @@ nobarrier This option can be used if underlying storage guarantees
|
|||
If this option is set, no cache_flush commands are issued
|
||||
but f2fs still guarantees the write ordering of all the
|
||||
data writes.
|
||||
fastboot This option is used when a system wants to reduce mount
|
||||
time as much as possible, even though normal performance
|
||||
can be sacrificed.
|
||||
extent_cache Enable an extent cache based on rb-tree, it can cache
|
||||
as many as extent which map between contiguous logical
|
||||
address and physical address per inode, resulting in
|
||||
increasing the cache hit ratio. Set by default.
|
||||
noextent_cache Disable an extent cache based on rb-tree explicitly, see
|
||||
the above extent_cache mount option.
|
||||
noinline_data Disable the inline data feature, inline data feature is
|
||||
enabled by default.
|
||||
data_flush Enable data flushing before checkpoint in order to
|
||||
persist data of regular and symlink.
|
||||
mode=%s Control block allocation mode which supports "adaptive"
|
||||
and "lfs". In "lfs" mode, there should be no random
|
||||
writes towards main area.
|
||||
|
||||
================================================================================
|
||||
DEBUGFS ENTRIES
|
||||
|
|
3
Makefile
3
Makefile
|
@ -621,9 +621,10 @@ all: vmlinux
|
|||
include $(srctree)/arch/$(SRCARCH)/Makefile
|
||||
|
||||
KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,)
|
||||
KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
|
||||
|
||||
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
|
||||
KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
|
||||
KBUILD_CFLAGS += -Os
|
||||
else
|
||||
KBUILD_CFLAGS += -O2
|
||||
endif
|
||||
|
|
4797
arch/arm64/configs/twrp_defconfig
Executable file
4797
arch/arm64/configs/twrp_defconfig
Executable file
File diff suppressed because it is too large
Load diff
6
drivers/gpu/msm/adreno_a5xx.c
Normal file → Executable file
6
drivers/gpu/msm/adreno_a5xx.c
Normal file → Executable file
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -379,6 +379,10 @@ static int a5xx_preemption_pre_ibsubmit(
|
|||
*cmds++ = 0;
|
||||
}
|
||||
|
||||
/* Enable CP_CONTEXT_SWITCH_YIELD packets in the IB2s */
|
||||
*cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1);
|
||||
*cmds++ = 2;
|
||||
|
||||
return cmds - cmds_orig;
|
||||
}
|
||||
|
||||
|
|
4
drivers/gpu/msm/adreno_ringbuffer.c
Normal file → Executable file
4
drivers/gpu/msm/adreno_ringbuffer.c
Normal file → Executable file
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2002,2007-2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -584,7 +584,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
|
|||
|
||||
if (gpudev->preemption_pre_ibsubmit &&
|
||||
adreno_is_preemption_enabled(adreno_dev))
|
||||
total_sizedwords += 20;
|
||||
total_sizedwords += 22;
|
||||
|
||||
if (gpudev->preemption_post_ibsubmit &&
|
||||
adreno_is_preemption_enabled(adreno_dev))
|
||||
|
|
4
drivers/gpu/msm/kgsl_sharedmem.c
Normal file → Executable file
4
drivers/gpu/msm/kgsl_sharedmem.c
Normal file → Executable file
|
@ -685,6 +685,10 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
|
|||
unsigned int align;
|
||||
unsigned int step = ((VMALLOC_END - VMALLOC_START)/8) >> PAGE_SHIFT;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
if (size == 0 || size > UINT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
align = (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
|
||||
|
||||
page_size = get_page_size(size, align);
|
||||
|
|
34
drivers/gpu/msm/kgsl_sync.c
Normal file → Executable file
34
drivers/gpu/msm/kgsl_sync.c
Normal file → Executable file
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -474,22 +474,22 @@ long kgsl_ioctl_syncsource_create(struct kgsl_device_private *dev_priv,
|
|||
goto out;
|
||||
}
|
||||
|
||||
kref_init(&syncsource->refcount);
|
||||
syncsource->private = private;
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock(&private->syncsource_lock);
|
||||
id = idr_alloc(&private->syncsource_idr, syncsource, 1, 0, GFP_NOWAIT);
|
||||
spin_unlock(&private->syncsource_lock);
|
||||
idr_preload_end();
|
||||
|
||||
if (id > 0) {
|
||||
kref_init(&syncsource->refcount);
|
||||
syncsource->id = id;
|
||||
syncsource->private = private;
|
||||
|
||||
param->id = id;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = id;
|
||||
}
|
||||
spin_unlock(&private->syncsource_lock);
|
||||
idr_preload_end();
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
|
@ -548,25 +548,23 @@ long kgsl_ioctl_syncsource_destroy(struct kgsl_device_private *dev_priv,
|
|||
{
|
||||
struct kgsl_syncsource_destroy *param = data;
|
||||
struct kgsl_syncsource *syncsource = NULL;
|
||||
struct kgsl_process_private *private;
|
||||
struct kgsl_process_private *private = dev_priv->process_priv;
|
||||
|
||||
syncsource = kgsl_syncsource_get(dev_priv->process_priv,
|
||||
param->id);
|
||||
spin_lock(&private->syncsource_lock);
|
||||
syncsource = idr_find(&private->syncsource_idr, param->id);
|
||||
|
||||
if (syncsource) {
|
||||
idr_remove(&private->syncsource_idr, param->id);
|
||||
syncsource->id = 0;
|
||||
}
|
||||
|
||||
spin_unlock(&private->syncsource_lock);
|
||||
|
||||
if (syncsource == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
private = syncsource->private;
|
||||
|
||||
spin_lock(&private->syncsource_lock);
|
||||
idr_remove(&private->syncsource_idr, param->id);
|
||||
syncsource->id = 0;
|
||||
spin_unlock(&private->syncsource_lock);
|
||||
|
||||
/* put reference from syncsource creation */
|
||||
kgsl_syncsource_put(syncsource);
|
||||
/* put reference from getting the syncsource above */
|
||||
kgsl_syncsource_put(syncsource);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
10
drivers/nfc/pn547.c
Normal file → Executable file
10
drivers/nfc/pn547.c
Normal file → Executable file
|
@ -661,7 +661,10 @@ long pn547_dev_ioctl(struct file *filp,
|
|||
}
|
||||
}
|
||||
if (!(current_state & P61_STATE_WIRED))
|
||||
{
|
||||
gpio_set_value(pn547_dev->ese_pwr_req, 0);
|
||||
msleep(60);
|
||||
}
|
||||
|
||||
pn547_dev->spi_ven_enabled = false;
|
||||
|
||||
|
@ -676,6 +679,7 @@ long pn547_dev_ioctl(struct file *filp,
|
|||
if (!(current_state & P61_STATE_WIRED))
|
||||
{
|
||||
gpio_set_value(pn547_dev->ese_pwr_req, 0);
|
||||
msleep(60);
|
||||
}
|
||||
/*If JCOP3.2 or 3.3 for handling triple mode protection signal NFC service */
|
||||
else
|
||||
|
@ -715,7 +719,7 @@ long pn547_dev_ioctl(struct file *filp,
|
|||
}
|
||||
}
|
||||
gpio_set_value(pn547_dev->ese_pwr_req, 0);
|
||||
msleep(10);
|
||||
msleep(60);
|
||||
gpio_set_value(pn547_dev->ese_pwr_req, 1);
|
||||
msleep(10);
|
||||
} else {
|
||||
|
@ -827,7 +831,10 @@ long pn547_dev_ioctl(struct file *filp,
|
|||
if (current_state & P61_STATE_WIRED){
|
||||
p61_update_access_state(pn547_dev, P61_STATE_WIRED, false);
|
||||
if((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0)
|
||||
{
|
||||
gpio_set_value(pn547_dev->ese_pwr_req, 0);
|
||||
msleep(60);
|
||||
}
|
||||
} else {
|
||||
pr_err("%s : P61_SET_WIRED_ACCESS - failed, current_state = %x \n",
|
||||
__func__, pn547_dev->p61_current_state);
|
||||
|
@ -839,6 +846,7 @@ long pn547_dev_ioctl(struct file *filp,
|
|||
{
|
||||
pr_info("%s : P61_ESE_GPIO_LOW \n", __func__);
|
||||
gpio_set_value(pn547_dev->ese_pwr_req, 0);
|
||||
msleep(60);
|
||||
}
|
||||
else if(arg == 3)
|
||||
{
|
||||
|
|
|
@ -2591,7 +2591,7 @@ static int mass_storage_function_init(struct android_usb_function *f,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
|
||||
fsg_mod_data.cdrom[0] = true;
|
||||
fsg_mod_data.cdrom[0] = false;
|
||||
fsg_mod_data.ro[0] = false;
|
||||
fsg_mod_data.removable[0] = true;
|
||||
#else
|
||||
|
|
|
@ -62,6 +62,8 @@ config FILE_LOCKING
|
|||
for filesystems like NFS and for the flock() system
|
||||
call. Disabling this option saves about 11k.
|
||||
|
||||
source "fs/crypto/Kconfig"
|
||||
|
||||
source "fs/notify/Kconfig"
|
||||
|
||||
source "fs/quota/Kconfig"
|
||||
|
|
|
@ -28,6 +28,7 @@ obj-$(CONFIG_SIGNALFD) += signalfd.o
|
|||
obj-$(CONFIG_TIMERFD) += timerfd.o
|
||||
obj-$(CONFIG_EVENTFD) += eventfd.o
|
||||
obj-$(CONFIG_AIO) += aio.o
|
||||
obj-$(CONFIG_FS_ENCRYPTION) += crypto/
|
||||
obj-$(CONFIG_FILE_LOCKING) += locks.o
|
||||
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
|
||||
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
|
||||
|
|
9
fs/aio.c
Normal file → Executable file
9
fs/aio.c
Normal file → Executable file
|
@ -1354,11 +1354,16 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb,
|
|||
unsigned long *nr_segs,
|
||||
struct iovec *iovec)
|
||||
{
|
||||
if (unlikely(!access_ok(!rw, buf, kiocb->ki_nbytes)))
|
||||
size_t len = kiocb->ki_nbytes;
|
||||
|
||||
if (len > MAX_RW_COUNT)
|
||||
len = MAX_RW_COUNT;
|
||||
|
||||
if (unlikely(!access_ok(!rw, buf, len)))
|
||||
return -EFAULT;
|
||||
|
||||
iovec->iov_base = buf;
|
||||
iovec->iov_len = kiocb->ki_nbytes;
|
||||
iovec->iov_len = len;
|
||||
*nr_segs = 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
18
fs/crypto/Kconfig
Normal file
18
fs/crypto/Kconfig
Normal 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
3
fs/crypto/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
|
||||
|
||||
fscrypto-y := crypto.o fname.o policy.o keyinfo.o
|
567
fs/crypto/crypto.c
Normal file
567
fs/crypto/crypto.c
Normal file
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* 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/crypto.h>
|
||||
#include <linux/ecryptfs.h>
|
||||
#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/namei.h>
|
||||
#include <linux/fscrypto.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
|
||||
* @gfp_flags: The gfp flag for memory allocation
|
||||
*
|
||||
* 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, gfp_t gfp_flags)
|
||||
{
|
||||
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_flags);
|
||||
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,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
u8 xts_tweak[FS_XTS_TWEAK_SIZE];
|
||||
struct ablkcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist dst, src;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
|
||||
req = ablkcipher_request_alloc(tfm, gfp_flags);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ablkcipher_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, &index, 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);
|
||||
ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
|
||||
xts_tweak);
|
||||
if (rw == FS_DECRYPT)
|
||||
res = crypto_ablkcipher_decrypt(req);
|
||||
else
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
ablkcipher_request_free(req);
|
||||
if (res) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_ablkcipher_encrypt() returned %d\n",
|
||||
__func__, res);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags)
|
||||
{
|
||||
ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags);
|
||||
if (ctx->w.bounce_page == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= FS_WRITE_PATH_FL;
|
||||
return ctx->w.bounce_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscypt_encrypt_page() - Encrypts a page
|
||||
* @inode: The inode for which the encryption should take place
|
||||
* @plaintext_page: The page to encrypt. Must be locked.
|
||||
* @gfp_flags: The gfp flag for memory allocation
|
||||
*
|
||||
* 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, gfp_t gfp_flags)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
int err;
|
||||
|
||||
BUG_ON(!PageLocked(plaintext_page));
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, gfp_flags);
|
||||
if (IS_ERR(ctx))
|
||||
return (struct page *)ctx;
|
||||
|
||||
/* The encryption operation will require a bounce page. */
|
||||
ciphertext_page = alloc_bounce_page(ctx, gfp_flags);
|
||||
if (IS_ERR(ciphertext_page))
|
||||
goto errout;
|
||||
|
||||
ctx->w.control_page = plaintext_page;
|
||||
err = do_page_crypto(inode, FS_ENCRYPT, plaintext_page->index,
|
||||
plaintext_page, ciphertext_page,
|
||||
gfp_flags);
|
||||
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, GFP_NOFS);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_page);
|
||||
|
||||
int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct bio *bio;
|
||||
int ret, err = 0;
|
||||
|
||||
BUG_ON(inode->i_sb->s_blocksize != PAGE_CACHE_SIZE);
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, GFP_NOFS);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT);
|
||||
if (IS_ERR(ciphertext_page)) {
|
||||
err = PTR_ERR(ciphertext_page);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
err = do_page_crypto(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), ciphertext_page,
|
||||
GFP_NOFS);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
bio = bio_alloc(GFP_NOWAIT, 1);
|
||||
if (!bio) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector =
|
||||
pblk << (inode->i_sb->s_blocksize_bits - 9);
|
||||
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);
|
||||
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 dentry *dir;
|
||||
struct fscrypt_info *ci;
|
||||
int dir_has_key, cached_with_key;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dget_parent(dentry);
|
||||
if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) {
|
||||
dput(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ci = d_inode(dir)->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))))
|
||||
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);
|
||||
dput(dir);
|
||||
|
||||
/*
|
||||
* 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");
|
427
fs/crypto/fname.c
Normal file
427
fs/crypto/fname.c
Normal file
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* This contains functions for filename crypto management
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* Written by Uday Savagaonkar, 2014.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*/
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/fscrypto.h>
|
||||
|
||||
static u32 size_round_up(size_t size, size_t blksize)
|
||||
{
|
||||
return ((size + blksize - 1) / blksize) * blksize;
|
||||
}
|
||||
|
||||
/**
|
||||
* dir_crypt_complete() -
|
||||
*/
|
||||
static void dir_crypt_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* fname_encrypt() -
|
||||
*
|
||||
* This function encrypts the input filename, and returns the length of the
|
||||
* ciphertext. Errors are returned as negative numbers. We trust the caller to
|
||||
* allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int fname_encrypt(struct inode *inode,
|
||||
const struct qstr *iname, struct fscrypt_str *oname)
|
||||
{
|
||||
u32 ciphertext_len;
|
||||
struct ablkcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
|
||||
char *workbuf, buf[32], *alloc_buf = NULL;
|
||||
unsigned lim;
|
||||
|
||||
lim = inode->i_sb->s_cop->max_namelen(inode);
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
ciphertext_len = (iname->len < FS_CRYPTO_BLOCK_SIZE) ?
|
||||
FS_CRYPTO_BLOCK_SIZE : iname->len;
|
||||
ciphertext_len = size_round_up(ciphertext_len, padding);
|
||||
ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
|
||||
|
||||
if (ciphertext_len <= sizeof(buf)) {
|
||||
workbuf = buf;
|
||||
} else {
|
||||
alloc_buf = kmalloc(ciphertext_len, GFP_NOFS);
|
||||
if (!alloc_buf)
|
||||
return -ENOMEM;
|
||||
workbuf = alloc_buf;
|
||||
}
|
||||
|
||||
/* Allocate request */
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n", __func__);
|
||||
kfree(alloc_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
dir_crypt_complete, &ecr);
|
||||
|
||||
/* Copy the input */
|
||||
memcpy(workbuf, iname->name, iname->len);
|
||||
if (iname->len < ciphertext_len)
|
||||
memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create encryption request */
|
||||
sg_init_one(&src_sg, workbuf, ciphertext_len);
|
||||
sg_init_one(&dst_sg, oname->name, ciphertext_len);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
kfree(alloc_buf);
|
||||
ablkcipher_request_free(req);
|
||||
if (res < 0)
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
|
||||
oname->len = ciphertext_len;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* fname_decrypt()
|
||||
* This function decrypts the input filename, and returns
|
||||
* the length of the plaintext.
|
||||
* Errors are returned as negative numbers.
|
||||
* We trust the caller to allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int fname_decrypt(struct inode *inode,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
struct ablkcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||
unsigned lim;
|
||||
|
||||
lim = inode->i_sb->s_cop->max_namelen(inode);
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
/* Allocate request */
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
dir_crypt_complete, &ecr);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create decryption request */
|
||||
sg_init_one(&src_sg, iname->name, iname->len);
|
||||
sg_init_one(&dst_sg, oname->name, oname->len);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
|
||||
res = crypto_ablkcipher_decrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
ablkcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
oname->len = strnlen(oname->name, iname->len);
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
/**
|
||||
* digest_encode() -
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static int digest_encode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
ac += (((unsigned char) src[i]) << bits);
|
||||
bits += 8;
|
||||
do {
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
ac >>= 6;
|
||||
bits -= 6;
|
||||
} while (bits >= 6);
|
||||
i++;
|
||||
}
|
||||
if (bits)
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
static int digest_decode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
const char *p;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
p = strchr(lookup_table, src[i]);
|
||||
if (p == NULL || src[i] == 0)
|
||||
return -2;
|
||||
ac += (p - lookup_table) << bits;
|
||||
bits += 6;
|
||||
if (bits >= 8) {
|
||||
*cp++ = ac & 0xff;
|
||||
ac >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ac)
|
||||
return -1;
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||
{
|
||||
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);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_crypto_alloc_obuff() -
|
||||
*
|
||||
* Allocates an output buffer that is sufficient for the crypto operation
|
||||
* specified by the context and the direction.
|
||||
*/
|
||||
int fscrypt_fname_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
|
||||
crypto_str->len = olen;
|
||||
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
/*
|
||||
* Allocated buffer can hold one more character to null-terminate the
|
||||
* string
|
||||
*/
|
||||
crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
|
||||
if (!(crypto_str->name))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_crypto_free_buffer() -
|
||||
*
|
||||
* Frees the buffer allocated for crypto operation.
|
||||
*/
|
||||
void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
|
||||
{
|
||||
if (!crypto_str)
|
||||
return;
|
||||
kfree(crypto_str->name);
|
||||
crypto_str->name = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_free_buffer);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_disk_to_usr() - converts a filename from disk space to user
|
||||
* space
|
||||
*/
|
||||
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
char buf[24];
|
||||
int ret;
|
||||
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
if (iname->len < FS_CRYPTO_BLOCK_SIZE)
|
||||
return -EUCLEAN;
|
||||
|
||||
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);
|
||||
oname->len = ret;
|
||||
return ret;
|
||||
}
|
||||
if (hash) {
|
||||
memcpy(buf, &hash, 4);
|
||||
memcpy(buf + 4, &minor_hash, 4);
|
||||
} else {
|
||||
memset(buf, 0, 8);
|
||||
}
|
||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||
oname->name[0] = '_';
|
||||
ret = digest_encode(buf, 24, oname->name + 1);
|
||||
oname->len = ret + 1;
|
||||
return ret + 1;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_usr_to_disk() - converts a filename from user space to disk
|
||||
* space
|
||||
*/
|
||||
int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
if (fscrypt_is_dot_dotdot(iname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
if (inode->i_crypt_info)
|
||||
return fname_encrypt(inode, iname, oname);
|
||||
/*
|
||||
* 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
|
||||
* a disk-space name
|
||||
*/
|
||||
return -EACCES;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||
|
||||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
int ret = 0, bigname = 0;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = 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.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
ret = get_crypt_info(dir);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
if (dir->i_crypt_info) {
|
||||
ret = fscrypt_fname_alloc_buffer(dir, iname->len,
|
||||
&fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
return 0;
|
||||
}
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
|
||||
/*
|
||||
* We don't have the key and we are doing a lookup; decode the
|
||||
* user-supplied name
|
||||
*/
|
||||
if (iname->name[0] == '_')
|
||||
bigname = 1;
|
||||
if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
|
||||
return -ENOENT;
|
||||
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (bigname) {
|
||||
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
||||
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
}
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
fscrypt_fname_free_buffer(&fname->crypto_buf);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_setup_filename);
|
||||
|
||||
void fscrypt_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_free_filename);
|
310
fs/crypto/keyinfo.c
Normal file
310
fs/crypto/keyinfo.c
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* 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 <crypto/hash.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 ablkcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0,
|
||||
0);
|
||||
|
||||
if (IS_ERR(tfm)) {
|
||||
res = PTR_ERR(tfm);
|
||||
tfm = NULL;
|
||||
goto out;
|
||||
}
|
||||
crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
derive_crypt_complete, &ecr);
|
||||
res = crypto_ablkcipher_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);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
FS_AES_256_XTS_KEY_SIZE, NULL);
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
out:
|
||||
if (req)
|
||||
ablkcipher_request_free(req);
|
||||
if (tfm)
|
||||
crypto_free_ablkcipher(tfm);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int validate_user_key(struct fscrypt_info *crypt_info,
|
||||
struct fscrypt_context *ctx, u8 *raw_key,
|
||||
u8 *prefix, int prefix_size)
|
||||
{
|
||||
u8 *full_key_descriptor;
|
||||
struct key *keyring_key;
|
||||
struct fscrypt_key *master_key;
|
||||
const struct user_key_payload *ukp;
|
||||
int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1;
|
||||
int res;
|
||||
|
||||
full_key_descriptor = kmalloc(full_key_len, GFP_NOFS);
|
||||
if (!full_key_descriptor)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(full_key_descriptor, prefix, prefix_size);
|
||||
sprintf(full_key_descriptor + prefix_size,
|
||||
"%*phN", FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx->master_key_descriptor);
|
||||
full_key_descriptor[full_key_len - 1] = '\0';
|
||||
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
|
||||
kfree(full_key_descriptor);
|
||||
if (IS_ERR(keyring_key))
|
||||
return PTR_ERR(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 = ((struct user_key_payload *)keyring_key->payload.data);
|
||||
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;
|
||||
|
||||
crypt_info->ci_keyring_key = keyring_key;
|
||||
return 0;
|
||||
out:
|
||||
key_put(keyring_key);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void put_crypt_info(struct fscrypt_info *ci)
|
||||
{
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
if (ci->ci_keyring_key)
|
||||
key_put(ci->ci_keyring_key);
|
||||
crypto_free_ablkcipher(ci->ci_ctfm);
|
||||
kmem_cache_free(fscrypt_info_cachep, ci);
|
||||
}
|
||||
|
||||
int get_crypt_info(struct inode *inode)
|
||||
{
|
||||
struct fscrypt_info *crypt_info;
|
||||
struct fscrypt_context ctx;
|
||||
struct crypto_ablkcipher *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;
|
||||
}
|
||||
|
||||
res = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
|
||||
if (res && inode->i_sb->s_cop->key_prefix) {
|
||||
u8 *prefix = NULL;
|
||||
int prefix_size, res2;
|
||||
|
||||
prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
|
||||
res2 = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
prefix, prefix_size);
|
||||
if (res2) {
|
||||
if (res2 == -ENOKEY)
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
} else if (res) {
|
||||
goto out;
|
||||
}
|
||||
got_key:
|
||||
ctfm = crypto_alloc_ablkcipher(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_ablkcipher_clear_flags(ctfm, ~0);
|
||||
crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm),
|
||||
CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
res = crypto_ablkcipher_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
229
fs/crypto/policy.c
Normal 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);
|
30
fs/ecryptfs/inode.c
Normal file → Executable file
30
fs/ecryptfs/inode.c
Normal file → Executable file
|
@ -48,6 +48,34 @@
|
|||
#include "ecryptfs_dlp.h"
|
||||
#endif
|
||||
|
||||
/* Do not directly use this function. Use ECRYPTFS_OVERRIDE_CRED() instead. */
|
||||
const struct cred * ecryptfs_override_fsids(uid_t fsuid, gid_t fsgid)
|
||||
{
|
||||
struct cred * cred;
|
||||
const struct cred * old_cred;
|
||||
|
||||
cred = prepare_creds();
|
||||
if (!cred)
|
||||
return NULL;
|
||||
|
||||
cred->fsuid = make_kuid(current_user_ns(), fsuid);
|
||||
cred->fsgid = make_kgid(current_user_ns(), fsgid);
|
||||
|
||||
old_cred = override_creds(cred);
|
||||
|
||||
return old_cred;
|
||||
}
|
||||
|
||||
/* Do not directly use this function, use REVERT_CRED() instead. */
|
||||
void ecryptfs_revert_fsids(const struct cred * old_cred)
|
||||
{
|
||||
const struct cred * cur_cred;
|
||||
|
||||
cur_cred = current->cred;
|
||||
revert_creds(old_cred);
|
||||
put_cred(cur_cred);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SDP
|
||||
static struct dentry *lock_parent(struct dentry *dentry)
|
||||
{
|
||||
|
@ -490,8 +518,6 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry,
|
|||
dentry_info->lower_path.dentry = lower_dentry;
|
||||
|
||||
if (!lower_dentry->d_inode) {
|
||||
/* We want to add because we couldn't find in lower */
|
||||
d_add(dentry, NULL);
|
||||
return 0;
|
||||
}
|
||||
inode = __ecryptfs_get_inode(lower_inode, dir_inode->i_sb);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
config F2FS_FS
|
||||
tristate "F2FS filesystem support (EXPERIMENTAL)"
|
||||
tristate "F2FS filesystem support"
|
||||
depends on BLOCK
|
||||
select CRYPTO
|
||||
select CRYPTO_CRC32
|
||||
help
|
||||
F2FS is based on Log-structured File System (LFS), which supports
|
||||
versatile "flash-friendly" features. The design has been focused on
|
||||
|
@ -45,7 +47,7 @@ config F2FS_FS_POSIX_ACL
|
|||
default y
|
||||
help
|
||||
Posix Access Control Lists (ACLs) support permissions for users and
|
||||
gourps beyond the owner/group/world scheme.
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
To learn more about Access Control Lists, visit the POSIX ACLs for
|
||||
Linux website <http://acl.bestbits.at/>.
|
||||
|
@ -71,3 +73,32 @@ config F2FS_CHECK_FS
|
|||
Enables BUG_ONs which check the filesystem consistency in runtime.
|
||||
|
||||
If you want to improve the performance, say N.
|
||||
|
||||
config F2FS_FS_ENCRYPTION
|
||||
bool "F2FS Encryption"
|
||||
depends on F2FS_FS
|
||||
depends on F2FS_FS_XATTR
|
||||
select FS_ENCRYPTION
|
||||
help
|
||||
Enable encryption of f2fs 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.
|
||||
|
||||
config F2FS_IO_TRACE
|
||||
bool "F2FS IO tracer"
|
||||
depends on F2FS_FS
|
||||
depends on FUNCTION_TRACER
|
||||
help
|
||||
F2FS IO trace is based on a function trace, which gathers process
|
||||
information and block IO patterns in the filesystem level.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config F2FS_FAULT_INJECTION
|
||||
bool "F2FS fault injection facility"
|
||||
depends on F2FS_FS
|
||||
help
|
||||
Test F2FS to inject faults such as ENOMEM, ENOSPC, and so on.
|
||||
|
||||
If unsure, say N.
|
||||
|
|
|
@ -2,6 +2,8 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o
|
|||
|
||||
f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o
|
||||
f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o
|
||||
f2fs-y += shrinker.o extent_cache.o
|
||||
f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
|
||||
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
|
||||
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
||||
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
|
||||
|
|
164
fs/f2fs/acl.c
164
fs/f2fs/acl.c
|
@ -62,7 +62,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
|||
if (count == 0)
|
||||
return NULL;
|
||||
|
||||
acl = posix_acl_alloc(count, GFP_KERNEL);
|
||||
acl = posix_acl_alloc(count, GFP_NOFS);
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -115,8 +115,8 @@ static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size)
|
|||
struct f2fs_acl_entry *entry;
|
||||
int i;
|
||||
|
||||
f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count *
|
||||
sizeof(struct f2fs_acl_entry), GFP_KERNEL);
|
||||
f2fs_acl = f2fs_kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count *
|
||||
sizeof(struct f2fs_acl_entry), GFP_NOFS);
|
||||
if (!f2fs_acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -162,7 +162,8 @@ fail:
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
||||
static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
|
||||
struct page *dpage)
|
||||
{
|
||||
int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
|
||||
void *value = NULL;
|
||||
|
@ -172,12 +173,13 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
|||
if (type == ACL_TYPE_ACCESS)
|
||||
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
|
||||
retval = f2fs_getxattr(inode, name_index, "", NULL, 0);
|
||||
retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
|
||||
if (retval > 0) {
|
||||
value = kmalloc(retval, GFP_F2FS_ZERO);
|
||||
value = f2fs_kmalloc(retval, GFP_F2FS_ZERO);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
retval = f2fs_getxattr(inode, name_index, "", value, retval);
|
||||
retval = f2fs_getxattr(inode, name_index, "", value,
|
||||
retval, dpage);
|
||||
}
|
||||
|
||||
if (retval > 0)
|
||||
|
@ -188,16 +190,17 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
|||
acl = ERR_PTR(retval);
|
||||
kfree(value);
|
||||
|
||||
if (!IS_ERR(acl))
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
return acl;
|
||||
}
|
||||
|
||||
struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
return __f2fs_get_acl(inode, type, NULL);
|
||||
}
|
||||
|
||||
static int __f2fs_set_acl(struct inode *inode, int type,
|
||||
struct posix_acl *acl, struct page *ipage)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
int name_index;
|
||||
void *value = NULL;
|
||||
size_t size = 0;
|
||||
|
@ -210,7 +213,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
|||
error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||||
if (error < 0)
|
||||
return error;
|
||||
set_acl_inode(fi, inode->i_mode);
|
||||
set_acl_inode(inode, inode->i_mode);
|
||||
if (error == 0)
|
||||
acl = NULL;
|
||||
}
|
||||
|
@ -229,7 +232,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
|||
if (acl) {
|
||||
value = f2fs_acl_to_disk(acl, &size);
|
||||
if (IS_ERR(value)) {
|
||||
cond_clear_inode_flag(fi, FI_ACL_MODE);
|
||||
clear_inode_flag(inode, FI_ACL_MODE);
|
||||
return (int)PTR_ERR(value);
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +243,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
|||
if (!error)
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
cond_clear_inode_flag(fi, FI_ACL_MODE);
|
||||
clear_inode_flag(inode, FI_ACL_MODE);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -249,22 +252,147 @@ int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||
return __f2fs_set_acl(inode, type, acl, NULL);
|
||||
}
|
||||
|
||||
int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage)
|
||||
/*
|
||||
* Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
|
||||
* are copied from posix_acl.c
|
||||
*/
|
||||
static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct posix_acl *default_acl, *acl;
|
||||
struct posix_acl *clone = NULL;
|
||||
|
||||
if (acl) {
|
||||
int size = sizeof(struct posix_acl) + acl->a_count *
|
||||
sizeof(struct posix_acl_entry);
|
||||
clone = kmemdup(acl, size, flags);
|
||||
if (clone)
|
||||
atomic_set(&clone->a_refcount, 1);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
|
||||
{
|
||||
struct posix_acl_entry *pa, *pe;
|
||||
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
|
||||
umode_t mode = *mode_p;
|
||||
int not_equiv = 0;
|
||||
|
||||
/* assert(atomic_read(acl->a_refcount) == 1); */
|
||||
|
||||
FOREACH_ACL_ENTRY(pa, acl, pe) {
|
||||
switch(pa->e_tag) {
|
||||
case ACL_USER_OBJ:
|
||||
pa->e_perm &= (mode >> 6) | ~S_IRWXO;
|
||||
mode &= (pa->e_perm << 6) | ~S_IRWXU;
|
||||
break;
|
||||
|
||||
case ACL_USER:
|
||||
case ACL_GROUP:
|
||||
not_equiv = 1;
|
||||
break;
|
||||
|
||||
case ACL_GROUP_OBJ:
|
||||
group_obj = pa;
|
||||
break;
|
||||
|
||||
case ACL_OTHER:
|
||||
pa->e_perm &= mode | ~S_IRWXO;
|
||||
mode &= pa->e_perm | ~S_IRWXO;
|
||||
break;
|
||||
|
||||
case ACL_MASK:
|
||||
mask_obj = pa;
|
||||
not_equiv = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask_obj) {
|
||||
mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
|
||||
mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
|
||||
} else {
|
||||
if (!group_obj)
|
||||
return -EIO;
|
||||
group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
|
||||
mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
|
||||
}
|
||||
|
||||
*mode_p = (*mode_p & ~S_IRWXUGO) | mode;
|
||||
return not_equiv;
|
||||
}
|
||||
|
||||
static int f2fs_acl_create(struct inode *dir, umode_t *mode,
|
||||
struct posix_acl **default_acl, struct posix_acl **acl,
|
||||
struct page *dpage)
|
||||
{
|
||||
struct posix_acl *p;
|
||||
struct posix_acl *clone;
|
||||
int ret;
|
||||
|
||||
*acl = NULL;
|
||||
*default_acl = NULL;
|
||||
|
||||
if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
|
||||
return 0;
|
||||
|
||||
p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
|
||||
if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
|
||||
*mode &= ~current_umask();
|
||||
return 0;
|
||||
}
|
||||
if (IS_ERR(p))
|
||||
return PTR_ERR(p);
|
||||
|
||||
clone = f2fs_acl_clone(p, GFP_NOFS);
|
||||
if (!clone)
|
||||
goto no_mem;
|
||||
|
||||
ret = f2fs_acl_create_masq(clone, mode);
|
||||
if (ret < 0)
|
||||
goto no_mem_clone;
|
||||
|
||||
if (ret == 0)
|
||||
posix_acl_release(clone);
|
||||
else
|
||||
*acl = clone;
|
||||
|
||||
if (!S_ISDIR(*mode))
|
||||
posix_acl_release(p);
|
||||
else
|
||||
*default_acl = p;
|
||||
|
||||
return 0;
|
||||
|
||||
no_mem_clone:
|
||||
posix_acl_release(clone);
|
||||
no_mem:
|
||||
posix_acl_release(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
|
||||
struct page *dpage)
|
||||
{
|
||||
struct posix_acl *default_acl = NULL, *acl = NULL;
|
||||
int error = 0;
|
||||
|
||||
error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
|
||||
error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
|
||||
if (default_acl) {
|
||||
error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
|
||||
ipage);
|
||||
posix_acl_release(default_acl);
|
||||
}
|
||||
if (acl) {
|
||||
if (error)
|
||||
if (!error)
|
||||
error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
|
||||
ipage);
|
||||
posix_acl_release(acl);
|
||||
|
|
|
@ -37,15 +37,16 @@ struct f2fs_acl_header {
|
|||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||
|
||||
extern struct posix_acl *f2fs_get_acl(struct inode *, int);
|
||||
extern int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
||||
extern int f2fs_init_acl(struct inode *, struct inode *, struct page *);
|
||||
extern int f2fs_set_acl(struct inode *, struct posix_acl *, int);
|
||||
extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
|
||||
struct page *);
|
||||
#else
|
||||
#define f2fs_check_acl NULL
|
||||
#define f2fs_get_acl NULL
|
||||
#define f2fs_set_acl NULL
|
||||
|
||||
static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
|
||||
struct page *page)
|
||||
struct page *ipage, struct page *dpage)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
1838
fs/f2fs/data.c
1838
fs/f2fs/data.c
File diff suppressed because it is too large
Load diff
166
fs/f2fs/debug.c
166
fs/f2fs/debug.c
|
@ -33,19 +33,33 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
int i;
|
||||
|
||||
/* validation check of the segment numbers */
|
||||
si->hit_ext = sbi->read_hit_ext;
|
||||
si->total_ext = sbi->total_hit_ext;
|
||||
si->hit_largest = atomic64_read(&sbi->read_hit_largest);
|
||||
si->hit_cached = atomic64_read(&sbi->read_hit_cached);
|
||||
si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree);
|
||||
si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree;
|
||||
si->total_ext = atomic64_read(&sbi->total_hit_ext);
|
||||
si->ext_tree = atomic_read(&sbi->total_ext_tree);
|
||||
si->zombie_tree = atomic_read(&sbi->total_zombie_tree);
|
||||
si->ext_node = atomic_read(&sbi->total_ext_node);
|
||||
si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES);
|
||||
si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS);
|
||||
si->ndirty_dirs = sbi->n_dirty_dirs;
|
||||
si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META);
|
||||
si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA);
|
||||
si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE];
|
||||
si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
|
||||
si->ndirty_all = sbi->ndirty_inode[DIRTY_META];
|
||||
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
|
||||
si->wb_bios = atomic_read(&sbi->nr_wb_bios);
|
||||
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
|
||||
si->rsvd_segs = reserved_segments(sbi);
|
||||
si->overp_segs = overprovision_segments(sbi);
|
||||
si->valid_count = valid_user_blocks(sbi);
|
||||
si->valid_node_count = valid_node_count(sbi);
|
||||
si->valid_inode_count = valid_inode_count(sbi);
|
||||
si->inline_inode = sbi->inline_inode;
|
||||
si->inline_xattr = atomic_read(&sbi->inline_xattr);
|
||||
si->inline_inode = atomic_read(&sbi->inline_inode);
|
||||
si->inline_dir = atomic_read(&sbi->inline_dir);
|
||||
si->orphans = sbi->im[ORPHAN_INO].ino_num;
|
||||
si->utilization = utilization(sbi);
|
||||
|
||||
si->free_segs = free_segments(sbi);
|
||||
|
@ -55,7 +69,9 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->node_pages = NODE_MAPPING(sbi)->nrpages;
|
||||
si->meta_pages = META_MAPPING(sbi)->nrpages;
|
||||
si->nats = NM_I(sbi)->nat_cnt;
|
||||
si->sits = SIT_I(sbi)->dirty_sentries;
|
||||
si->dirty_nats = NM_I(sbi)->dirty_nat_cnt;
|
||||
si->sits = MAIN_SEGS(sbi);
|
||||
si->dirty_sits = SIT_I(sbi)->dirty_sentries;
|
||||
si->fnids = NM_I(sbi)->fcnt;
|
||||
si->bg_gc = sbi->bg_gc;
|
||||
si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg)
|
||||
|
@ -77,6 +93,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->segment_count[i] = sbi->segment_count[i];
|
||||
si->block_count[i] = sbi->block_count[i];
|
||||
}
|
||||
|
||||
si->inplace_count = atomic_read(&sbi->inplace_count);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -85,13 +103,14 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
static void update_sit_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||
unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
|
||||
unsigned long long blks_per_sec, hblks_per_sec, total_vblocks;
|
||||
unsigned long long bimodal, dist;
|
||||
unsigned int segno, vblocks;
|
||||
int ndirty = 0;
|
||||
|
||||
bimodal = 0;
|
||||
total_vblocks = 0;
|
||||
blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg);
|
||||
blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
|
||||
hblks_per_sec = blks_per_sec / 2;
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
|
||||
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
||||
|
@ -103,10 +122,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
|
|||
ndirty++;
|
||||
}
|
||||
}
|
||||
dist = MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100;
|
||||
si->bimodal = bimodal / dist;
|
||||
dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100);
|
||||
si->bimodal = div64_u64(bimodal, dist);
|
||||
if (si->dirty_count)
|
||||
si->avg_vblocks = total_vblocks / ndirty;
|
||||
si->avg_vblocks = div_u64(total_vblocks, ndirty);
|
||||
else
|
||||
si->avg_vblocks = 0;
|
||||
}
|
||||
|
@ -118,6 +137,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
{
|
||||
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||
unsigned npages;
|
||||
int i;
|
||||
|
||||
if (si->base_mem)
|
||||
goto get_cache;
|
||||
|
@ -125,6 +145,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize;
|
||||
si->base_mem += 2 * sizeof(struct f2fs_inode_info);
|
||||
si->base_mem += sizeof(*sbi->ckpt);
|
||||
si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE;
|
||||
|
||||
/* build sm */
|
||||
si->base_mem += sizeof(struct f2fs_sm_info);
|
||||
|
@ -133,7 +154,8 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
si->base_mem += sizeof(struct sit_info);
|
||||
si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry);
|
||||
si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi));
|
||||
si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi);
|
||||
si->base_mem += 3 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi);
|
||||
si->base_mem += SIT_VBLOCK_MAP_SIZE;
|
||||
if (sbi->segs_per_sec > 1)
|
||||
si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry);
|
||||
si->base_mem += __bitmap_size(sbi, SIT_BITMAP);
|
||||
|
@ -145,7 +167,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
|
||||
/* build curseg */
|
||||
si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE;
|
||||
si->base_mem += PAGE_CACHE_SIZE * NR_CURSEG_TYPE;
|
||||
si->base_mem += PAGE_SIZE * NR_CURSEG_TYPE;
|
||||
|
||||
/* build dirty segmap */
|
||||
si->base_mem += sizeof(struct dirty_seglist_info);
|
||||
|
@ -156,19 +178,35 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
si->base_mem += sizeof(struct f2fs_nm_info);
|
||||
si->base_mem += __bitmap_size(sbi, NAT_BITMAP);
|
||||
|
||||
/* build gc */
|
||||
si->base_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
get_cache:
|
||||
si->cache_mem = 0;
|
||||
|
||||
/* build gc */
|
||||
if (sbi->gc_thread)
|
||||
si->cache_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
/* build merge flush thread */
|
||||
if (SM_I(sbi)->cmd_control_info)
|
||||
si->cache_mem += sizeof(struct flush_cmd_control);
|
||||
|
||||
/* free nids */
|
||||
si->cache_mem = NM_I(sbi)->fcnt;
|
||||
si->cache_mem += NM_I(sbi)->nat_cnt;
|
||||
si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid);
|
||||
si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry);
|
||||
si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
|
||||
sizeof(struct nat_entry_set);
|
||||
si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
|
||||
for (i = 0; i <= ORPHAN_INO; i++)
|
||||
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
|
||||
si->cache_mem += atomic_read(&sbi->total_ext_tree) *
|
||||
sizeof(struct extent_tree);
|
||||
si->cache_mem += atomic_read(&sbi->total_ext_node) *
|
||||
sizeof(struct extent_node);
|
||||
|
||||
si->page_mem = 0;
|
||||
npages = NODE_MAPPING(sbi)->nrpages;
|
||||
si->cache_mem += npages << PAGE_CACHE_SHIFT;
|
||||
si->page_mem += (unsigned long long)npages << PAGE_SHIFT;
|
||||
npages = META_MAPPING(sbi)->nrpages;
|
||||
si->cache_mem += npages << PAGE_CACHE_SHIFT;
|
||||
si->cache_mem += sbi->n_orphans * sizeof(struct ino_entry);
|
||||
si->cache_mem += sbi->n_dirty_dirs * sizeof(struct dir_inode_entry);
|
||||
si->page_mem += (unsigned long long)npages << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static int stat_show(struct seq_file *s, void *v)
|
||||
|
@ -183,8 +221,9 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
|
||||
update_general_status(si->sbi);
|
||||
|
||||
seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n",
|
||||
bdevname(si->sbi->sb->s_bdev, devname), i++);
|
||||
seq_printf(s, "\n=====[ partition info(%s). #%d, %s]=====\n",
|
||||
bdevname(si->sbi->sb->s_bdev, devname), i++,
|
||||
f2fs_readonly(si->sbi->sb) ? "RO": "RW");
|
||||
seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ",
|
||||
si->sit_area_segs, si->nat_area_segs);
|
||||
seq_printf(s, "[SSA: %d] [MAIN: %d",
|
||||
|
@ -198,8 +237,14 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
seq_printf(s, "Other: %u)\n - Data: %u\n",
|
||||
si->valid_node_count - si->valid_inode_count,
|
||||
si->valid_count - si->valid_node_count);
|
||||
seq_printf(s, " - Inline_xattr Inode: %u\n",
|
||||
si->inline_xattr);
|
||||
seq_printf(s, " - Inline_data Inode: %u\n",
|
||||
si->inline_inode);
|
||||
seq_printf(s, " - Inline_dentry Inode: %u\n",
|
||||
si->inline_dir);
|
||||
seq_printf(s, " - Orphan Inode: %u\n",
|
||||
si->orphans);
|
||||
seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n",
|
||||
si->main_area_segs, si->main_area_sections,
|
||||
si->main_area_zones);
|
||||
|
@ -233,25 +278,43 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
si->dirty_count);
|
||||
seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n",
|
||||
si->prefree_count, si->free_segs, si->free_secs);
|
||||
seq_printf(s, "CP calls: %d\n", si->cp_count);
|
||||
seq_printf(s, "CP calls: %d (BG: %d)\n",
|
||||
si->cp_count, si->bg_cp_count);
|
||||
seq_printf(s, "GC calls: %d (BG: %d)\n",
|
||||
si->call_count, si->bg_gc);
|
||||
seq_printf(s, " - data segments : %d\n", si->data_segs);
|
||||
seq_printf(s, " - node segments : %d\n", si->node_segs);
|
||||
seq_printf(s, "Try to move %d blocks\n", si->tot_blks);
|
||||
seq_printf(s, " - data blocks : %d\n", si->data_blks);
|
||||
seq_printf(s, " - node blocks : %d\n", si->node_blks);
|
||||
seq_printf(s, "\nExtent Hit Ratio: %d / %d\n",
|
||||
si->hit_ext, si->total_ext);
|
||||
seq_printf(s, " - data segments : %d (%d)\n",
|
||||
si->data_segs, si->bg_data_segs);
|
||||
seq_printf(s, " - node segments : %d (%d)\n",
|
||||
si->node_segs, si->bg_node_segs);
|
||||
seq_printf(s, "Try to move %d blocks (BG: %d)\n", si->tot_blks,
|
||||
si->bg_data_blks + si->bg_node_blks);
|
||||
seq_printf(s, " - data blocks : %d (%d)\n", si->data_blks,
|
||||
si->bg_data_blks);
|
||||
seq_printf(s, " - node blocks : %d (%d)\n", si->node_blks,
|
||||
si->bg_node_blks);
|
||||
seq_puts(s, "\nExtent Cache:\n");
|
||||
seq_printf(s, " - Hit Count: L1-1:%llu L1-2:%llu L2:%llu\n",
|
||||
si->hit_largest, si->hit_cached,
|
||||
si->hit_rbtree);
|
||||
seq_printf(s, " - Hit Ratio: %llu%% (%llu / %llu)\n",
|
||||
!si->total_ext ? 0 :
|
||||
div64_u64(si->hit_total * 100, si->total_ext),
|
||||
si->hit_total, si->total_ext);
|
||||
seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
|
||||
si->ext_tree, si->zombie_tree, si->ext_node);
|
||||
seq_puts(s, "\nBalancing F2FS Async:\n");
|
||||
seq_printf(s, " - nodes: %4d in %4d\n",
|
||||
seq_printf(s, " - inmem: %4lld, wb_bios: %4d\n",
|
||||
si->inmem_pages, si->wb_bios);
|
||||
seq_printf(s, " - nodes: %4lld in %4d\n",
|
||||
si->ndirty_node, si->node_pages);
|
||||
seq_printf(s, " - dents: %4d in dirs:%4d\n",
|
||||
si->ndirty_dent, si->ndirty_dirs);
|
||||
seq_printf(s, " - meta: %4d in %4d\n",
|
||||
seq_printf(s, " - dents: %4lld in dirs:%4d (%4d)\n",
|
||||
si->ndirty_dent, si->ndirty_dirs, si->ndirty_all);
|
||||
seq_printf(s, " - datas: %4lld in files:%4d\n",
|
||||
si->ndirty_data, si->ndirty_files);
|
||||
seq_printf(s, " - meta: %4lld in %4d\n",
|
||||
si->ndirty_meta, si->meta_pages);
|
||||
seq_printf(s, " - NATs: %9d\n - SITs: %9d\n",
|
||||
si->nats, si->sits);
|
||||
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
|
||||
si->dirty_nats, si->nats, si->dirty_sits, si->sits);
|
||||
seq_printf(s, " - free_nids: %9d\n",
|
||||
si->fnids);
|
||||
seq_puts(s, "\nDistribution of User Blocks:");
|
||||
|
@ -269,6 +332,7 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
for (j = 0; j < si->util_free; j++)
|
||||
seq_putc(s, '-');
|
||||
seq_puts(s, "]\n\n");
|
||||
seq_printf(s, "IPU: %u blocks\n", si->inplace_count);
|
||||
seq_printf(s, "SSR: %u blocks in %u segments\n",
|
||||
si->block_count[SSR], si->segment_count[SSR]);
|
||||
seq_printf(s, "LFS: %u blocks in %u segments\n",
|
||||
|
@ -281,9 +345,14 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
|
||||
/* memory footprint */
|
||||
update_mem_info(si->sbi);
|
||||
seq_printf(s, "\nMemory: %u KB = static: %u + cached: %u\n",
|
||||
(si->base_mem + si->cache_mem) >> 10,
|
||||
si->base_mem >> 10, si->cache_mem >> 10);
|
||||
seq_printf(s, "\nMemory: %llu KB\n",
|
||||
(si->base_mem + si->cache_mem + si->page_mem) >> 10);
|
||||
seq_printf(s, " - static: %llu KB\n",
|
||||
si->base_mem >> 10);
|
||||
seq_printf(s, " - cached: %llu KB\n",
|
||||
si->cache_mem >> 10);
|
||||
seq_printf(s, " - paged : %llu KB\n",
|
||||
si->page_mem >> 10);
|
||||
}
|
||||
mutex_unlock(&f2fs_stat_mutex);
|
||||
return 0;
|
||||
|
@ -321,6 +390,16 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
|||
si->sbi = sbi;
|
||||
sbi->stat_info = si;
|
||||
|
||||
atomic64_set(&sbi->total_hit_ext, 0);
|
||||
atomic64_set(&sbi->read_hit_rbtree, 0);
|
||||
atomic64_set(&sbi->read_hit_largest, 0);
|
||||
atomic64_set(&sbi->read_hit_cached, 0);
|
||||
|
||||
atomic_set(&sbi->inline_xattr, 0);
|
||||
atomic_set(&sbi->inline_inode, 0);
|
||||
atomic_set(&sbi->inline_dir, 0);
|
||||
atomic_set(&sbi->inplace_count, 0);
|
||||
|
||||
mutex_lock(&f2fs_stat_mutex);
|
||||
list_add_tail(&si->stat_list, &f2fs_stat_list);
|
||||
mutex_unlock(&f2fs_stat_mutex);
|
||||
|
@ -339,20 +418,23 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
|
|||
kfree(si);
|
||||
}
|
||||
|
||||
void __init f2fs_create_root_stats(void)
|
||||
int __init f2fs_create_root_stats(void)
|
||||
{
|
||||
struct dentry *file;
|
||||
|
||||
f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL);
|
||||
if (!f2fs_debugfs_root)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root,
|
||||
NULL, &stat_fops);
|
||||
if (!file) {
|
||||
debugfs_remove(f2fs_debugfs_root);
|
||||
f2fs_debugfs_root = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_destroy_root_stats(void)
|
||||
|
|
666
fs/f2fs/dir.c
666
fs/f2fs/dir.c
File diff suppressed because it is too large
Load diff
749
fs/f2fs/extent_cache.c
Normal file
749
fs/f2fs/extent_cache.c
Normal file
|
@ -0,0 +1,749 @@
|
|||
/*
|
||||
* f2fs extent cache support
|
||||
*
|
||||
* Copyright (c) 2015 Motorola Mobility
|
||||
* Copyright (c) 2015 Samsung Electronics
|
||||
* Authors: Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
* Chao Yu <chao2.yu@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static struct kmem_cache *extent_tree_slab;
|
||||
static struct kmem_cache *extent_node_slab;
|
||||
|
||||
static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct rb_node *parent, struct rb_node **p)
|
||||
{
|
||||
struct extent_node *en;
|
||||
|
||||
en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
en->ei = *ei;
|
||||
INIT_LIST_HEAD(&en->list);
|
||||
en->et = et;
|
||||
|
||||
rb_link_node(&en->rb_node, parent, p);
|
||||
rb_insert_color(&en->rb_node, &et->root);
|
||||
atomic_inc(&et->node_cnt);
|
||||
atomic_inc(&sbi->total_ext_node);
|
||||
return en;
|
||||
}
|
||||
|
||||
static void __detach_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_node *en)
|
||||
{
|
||||
rb_erase(&en->rb_node, &et->root);
|
||||
atomic_dec(&et->node_cnt);
|
||||
atomic_dec(&sbi->total_ext_node);
|
||||
|
||||
if (et->cached_en == en)
|
||||
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)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et;
|
||||
nid_t ino = inode->i_ino;
|
||||
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
et = radix_tree_lookup(&sbi->extent_tree_root, ino);
|
||||
if (!et) {
|
||||
et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS);
|
||||
f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et);
|
||||
memset(et, 0, sizeof(struct extent_tree));
|
||||
et->ino = ino;
|
||||
et->root = RB_ROOT;
|
||||
et->cached_en = NULL;
|
||||
rwlock_init(&et->lock);
|
||||
INIT_LIST_HEAD(&et->list);
|
||||
atomic_set(&et->node_cnt, 0);
|
||||
atomic_inc(&sbi->total_ext_tree);
|
||||
} else {
|
||||
atomic_dec(&sbi->total_zombie_tree);
|
||||
list_del_init(&et->list);
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
/* never died until evict_inode */
|
||||
F2FS_I(inode)->extent_tree = et;
|
||||
|
||||
return et;
|
||||
}
|
||||
|
||||
static struct extent_node *__lookup_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, unsigned int fofs)
|
||||
{
|
||||
struct rb_node *node = et->root.rb_node;
|
||||
struct extent_node *en = et->cached_en;
|
||||
|
||||
if (en) {
|
||||
struct extent_info *cei = &en->ei;
|
||||
|
||||
if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) {
|
||||
stat_inc_cached_node_hit(sbi);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
while (node) {
|
||||
en = rb_entry(node, struct extent_node, rb_node);
|
||||
|
||||
if (fofs < en->ei.fofs) {
|
||||
node = node->rb_left;
|
||||
} else if (fofs >= en->ei.fofs + en->ei.len) {
|
||||
node = node->rb_right;
|
||||
} else {
|
||||
stat_inc_rbtree_node_hit(sbi);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei)
|
||||
{
|
||||
struct rb_node **p = &et->root.rb_node;
|
||||
struct extent_node *en;
|
||||
|
||||
en = __attach_extent_node(sbi, et, ei, NULL, p);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
et->largest = en->ei;
|
||||
et->cached_en = en;
|
||||
return en;
|
||||
}
|
||||
|
||||
static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et)
|
||||
{
|
||||
struct rb_node *node, *next;
|
||||
struct extent_node *en;
|
||||
unsigned int count = atomic_read(&et->node_cnt);
|
||||
|
||||
node = rb_first(&et->root);
|
||||
while (node) {
|
||||
next = rb_next(node);
|
||||
en = rb_entry(node, struct extent_node, rb_node);
|
||||
__release_extent_node(sbi, et, en);
|
||||
node = next;
|
||||
}
|
||||
|
||||
return count - atomic_read(&et->node_cnt);
|
||||
}
|
||||
|
||||
static void __drop_largest_extent(struct inode *inode,
|
||||
pgoff_t fofs, unsigned int len)
|
||||
{
|
||||
struct extent_info *largest = &F2FS_I(inode)->extent_tree->largest;
|
||||
|
||||
if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) {
|
||||
largest->len = 0;
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
}
|
||||
}
|
||||
|
||||
/* return true, if inode page is changed */
|
||||
bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et;
|
||||
struct extent_node *en;
|
||||
struct extent_info ei;
|
||||
|
||||
if (!f2fs_may_extent_tree(inode)) {
|
||||
/* drop largest extent */
|
||||
if (i_ext && i_ext->len) {
|
||||
i_ext->len = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
et = __grab_extent_tree(inode);
|
||||
|
||||
if (!i_ext || !i_ext->len)
|
||||
return false;
|
||||
|
||||
get_extent_info(&ei, i_ext);
|
||||
|
||||
write_lock(&et->lock);
|
||||
if (atomic_read(&et->node_cnt))
|
||||
goto out;
|
||||
|
||||
en = __init_extent_tree(sbi, et, &ei);
|
||||
if (en) {
|
||||
spin_lock(&sbi->extent_lock);
|
||||
list_add_tail(&en->list, &sbi->extent_list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
}
|
||||
out:
|
||||
write_unlock(&et->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
||||
struct extent_info *ei)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
struct extent_node *en;
|
||||
bool ret = false;
|
||||
|
||||
f2fs_bug_on(sbi, !et);
|
||||
|
||||
trace_f2fs_lookup_extent_tree_start(inode, pgofs);
|
||||
|
||||
read_lock(&et->lock);
|
||||
|
||||
if (et->largest.fofs <= pgofs &&
|
||||
et->largest.fofs + et->largest.len > pgofs) {
|
||||
*ei = et->largest;
|
||||
ret = true;
|
||||
stat_inc_largest_node_hit(sbi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
en = __lookup_extent_tree(sbi, et, pgofs);
|
||||
if (en) {
|
||||
*ei = en->ei;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
ret = true;
|
||||
}
|
||||
out:
|
||||
stat_inc_total_hit(sbi);
|
||||
read_unlock(&et->lock);
|
||||
|
||||
trace_f2fs_lookup_extent_tree_end(inode, pgofs, ei);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lookup extent at @fofs, if hit, return the extent
|
||||
* if not, return NULL and
|
||||
* @prev_ex: extent before fofs
|
||||
* @next_ex: extent after fofs
|
||||
* @insert_p: insert point for new extent at fofs
|
||||
* in order to simpfy the insertion after.
|
||||
* tree must stay unchanged between lookup and insertion.
|
||||
*/
|
||||
static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et,
|
||||
unsigned int fofs,
|
||||
struct extent_node **prev_ex,
|
||||
struct extent_node **next_ex,
|
||||
struct rb_node ***insert_p,
|
||||
struct rb_node **insert_parent)
|
||||
{
|
||||
struct rb_node **pnode = &et->root.rb_node;
|
||||
struct rb_node *parent = NULL, *tmp_node;
|
||||
struct extent_node *en = et->cached_en;
|
||||
|
||||
*insert_p = NULL;
|
||||
*insert_parent = NULL;
|
||||
*prev_ex = NULL;
|
||||
*next_ex = NULL;
|
||||
|
||||
if (RB_EMPTY_ROOT(&et->root))
|
||||
return NULL;
|
||||
|
||||
if (en) {
|
||||
struct extent_info *cei = &en->ei;
|
||||
|
||||
if (cei->fofs <= fofs && cei->fofs + cei->len > fofs)
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
while (*pnode) {
|
||||
parent = *pnode;
|
||||
en = rb_entry(*pnode, struct extent_node, rb_node);
|
||||
|
||||
if (fofs < en->ei.fofs)
|
||||
pnode = &(*pnode)->rb_left;
|
||||
else if (fofs >= en->ei.fofs + en->ei.len)
|
||||
pnode = &(*pnode)->rb_right;
|
||||
else
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
*insert_p = pnode;
|
||||
*insert_parent = parent;
|
||||
|
||||
en = rb_entry(parent, struct extent_node, rb_node);
|
||||
tmp_node = parent;
|
||||
if (parent && fofs > en->ei.fofs)
|
||||
tmp_node = rb_next(parent);
|
||||
*next_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
|
||||
tmp_node = parent;
|
||||
if (parent && fofs < en->ei.fofs)
|
||||
tmp_node = rb_prev(parent);
|
||||
*prev_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
return NULL;
|
||||
|
||||
lookup_neighbors:
|
||||
if (fofs == en->ei.fofs) {
|
||||
/* lookup prev node for merging backward later */
|
||||
tmp_node = rb_prev(&en->rb_node);
|
||||
*prev_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
}
|
||||
if (fofs == en->ei.fofs + en->ei.len - 1) {
|
||||
/* lookup next node for merging frontward later */
|
||||
tmp_node = rb_next(&en->rb_node);
|
||||
*next_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
}
|
||||
return en;
|
||||
}
|
||||
|
||||
static struct extent_node *__try_merge_extent_node(struct inode *inode,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct extent_node *prev_ex,
|
||||
struct extent_node *next_ex)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_node *en = NULL;
|
||||
|
||||
if (prev_ex && __is_back_mergeable(ei, &prev_ex->ei)) {
|
||||
prev_ex->ei.len += ei->len;
|
||||
ei = &prev_ex->ei;
|
||||
en = prev_ex;
|
||||
}
|
||||
|
||||
if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) {
|
||||
if (en)
|
||||
__release_extent_node(sbi, et, prev_ex);
|
||||
next_ex->ei.fofs = ei->fofs;
|
||||
next_ex->ei.blk = ei->blk;
|
||||
next_ex->ei.len += ei->len;
|
||||
en = next_ex;
|
||||
}
|
||||
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
__try_update_largest_extent(inode, et, en);
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
return en;
|
||||
}
|
||||
|
||||
static struct extent_node *__insert_extent_tree(struct inode *inode,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct rb_node **insert_p,
|
||||
struct rb_node *insert_parent)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct rb_node **p = &et->root.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct extent_node *en = NULL;
|
||||
|
||||
if (insert_p && insert_parent) {
|
||||
parent = insert_parent;
|
||||
p = insert_p;
|
||||
goto do_insert;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
en = rb_entry(parent, struct extent_node, rb_node);
|
||||
|
||||
if (ei->fofs < en->ei.fofs)
|
||||
p = &(*p)->rb_left;
|
||||
else if (ei->fofs >= en->ei.fofs + en->ei.len)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
do_insert:
|
||||
en = __attach_extent_node(sbi, et, ei, parent, p);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
__try_update_largest_extent(inode, et, en);
|
||||
|
||||
/* update in global extent list */
|
||||
spin_lock(&sbi->extent_lock);
|
||||
list_add_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
return en;
|
||||
}
|
||||
|
||||
static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
pgoff_t fofs, block_t blkaddr, unsigned int len)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
struct extent_node *en = NULL, *en1 = NULL;
|
||||
struct extent_node *prev_en = NULL, *next_en = NULL;
|
||||
struct extent_info ei, dei, prev;
|
||||
struct rb_node **insert_p = NULL, *insert_parent = NULL;
|
||||
unsigned int end = fofs + len;
|
||||
unsigned int pos = (unsigned int)fofs;
|
||||
|
||||
if (!et)
|
||||
return false;
|
||||
|
||||
trace_f2fs_update_extent_tree_range(inode, fofs, blkaddr, len);
|
||||
|
||||
write_lock(&et->lock);
|
||||
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
|
||||
write_unlock(&et->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = et->largest;
|
||||
dei.len = 0;
|
||||
|
||||
/*
|
||||
* drop largest extent before lookup, in case it's already
|
||||
* been shrunk from extent tree
|
||||
*/
|
||||
__drop_largest_extent(inode, fofs, len);
|
||||
|
||||
/* 1. lookup first extent node in range [fofs, fofs + len - 1] */
|
||||
en = __lookup_extent_tree_ret(et, fofs, &prev_en, &next_en,
|
||||
&insert_p, &insert_parent);
|
||||
if (!en)
|
||||
en = next_en;
|
||||
|
||||
/* 2. invlidate all extent nodes in range [fofs, fofs + len - 1] */
|
||||
while (en && en->ei.fofs < end) {
|
||||
unsigned int org_end;
|
||||
int parts = 0; /* # of parts current extent split into */
|
||||
|
||||
next_en = en1 = NULL;
|
||||
|
||||
dei = en->ei;
|
||||
org_end = dei.fofs + dei.len;
|
||||
f2fs_bug_on(sbi, pos >= org_end);
|
||||
|
||||
if (pos > dei.fofs && pos - dei.fofs >= F2FS_MIN_EXTENT_LEN) {
|
||||
en->ei.len = pos - en->ei.fofs;
|
||||
prev_en = en;
|
||||
parts = 1;
|
||||
}
|
||||
|
||||
if (end < org_end && org_end - end >= F2FS_MIN_EXTENT_LEN) {
|
||||
if (parts) {
|
||||
set_extent_info(&ei, end,
|
||||
end - dei.fofs + dei.blk,
|
||||
org_end - end);
|
||||
en1 = __insert_extent_tree(inode, et, &ei,
|
||||
NULL, NULL);
|
||||
next_en = en1;
|
||||
} else {
|
||||
en->ei.fofs = end;
|
||||
en->ei.blk += end - dei.fofs;
|
||||
en->ei.len -= end - dei.fofs;
|
||||
next_en = en;
|
||||
}
|
||||
parts++;
|
||||
}
|
||||
|
||||
if (!next_en) {
|
||||
struct rb_node *node = rb_next(&en->rb_node);
|
||||
|
||||
next_en = node ?
|
||||
rb_entry(node, struct extent_node, rb_node)
|
||||
: NULL;
|
||||
}
|
||||
|
||||
if (parts)
|
||||
__try_update_largest_extent(inode, et, en);
|
||||
else
|
||||
__release_extent_node(sbi, et, en);
|
||||
|
||||
/*
|
||||
* if original extent is split into zero or two parts, extent
|
||||
* tree has been altered by deletion or insertion, therefore
|
||||
* invalidate pointers regard to tree.
|
||||
*/
|
||||
if (parts != 1) {
|
||||
insert_p = NULL;
|
||||
insert_parent = NULL;
|
||||
}
|
||||
en = next_en;
|
||||
}
|
||||
|
||||
/* 3. update extent in extent cache */
|
||||
if (blkaddr) {
|
||||
|
||||
set_extent_info(&ei, fofs, blkaddr, len);
|
||||
if (!__try_merge_extent_node(inode, et, &ei, prev_en, next_en))
|
||||
__insert_extent_tree(inode, et, &ei,
|
||||
insert_p, insert_parent);
|
||||
|
||||
/* give up extent_cache, if split and small updates happen */
|
||||
if (dei.len >= 1 &&
|
||||
prev.len < F2FS_MIN_EXTENT_LEN &&
|
||||
et->largest.len < F2FS_MIN_EXTENT_LEN) {
|
||||
__drop_largest_extent(inode, 0, UINT_MAX);
|
||||
set_inode_flag(inode, FI_NO_EXTENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT))
|
||||
__free_extent_tree(sbi, et);
|
||||
|
||||
write_unlock(&et->lock);
|
||||
|
||||
return !__is_extent_same(&prev, &et->largest);
|
||||
}
|
||||
|
||||
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
{
|
||||
struct extent_tree *et, *next;
|
||||
struct extent_node *en;
|
||||
unsigned int node_cnt = 0, tree_cnt = 0;
|
||||
int remained;
|
||||
|
||||
if (!test_opt(sbi, EXTENT_CACHE))
|
||||
return 0;
|
||||
|
||||
if (!atomic_read(&sbi->total_zombie_tree))
|
||||
goto free_node;
|
||||
|
||||
if (!down_write_trylock(&sbi->extent_tree_lock))
|
||||
goto out;
|
||||
|
||||
/* 1. remove unreferenced extent tree */
|
||||
list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
|
||||
if (atomic_read(&et->node_cnt)) {
|
||||
write_lock(&et->lock);
|
||||
node_cnt += __free_extent_tree(sbi, et);
|
||||
write_unlock(&et->lock);
|
||||
}
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
list_del_init(&et->list);
|
||||
radix_tree_delete(&sbi->extent_tree_root, et->ino);
|
||||
kmem_cache_free(extent_tree_slab, et);
|
||||
atomic_dec(&sbi->total_ext_tree);
|
||||
atomic_dec(&sbi->total_zombie_tree);
|
||||
tree_cnt++;
|
||||
|
||||
if (node_cnt + tree_cnt >= nr_shrink)
|
||||
goto unlock_out;
|
||||
cond_resched();
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
free_node:
|
||||
/* 2. remove LRU extent entries */
|
||||
if (!down_write_trylock(&sbi->extent_tree_lock))
|
||||
goto out;
|
||||
|
||||
remained = nr_shrink - (node_cnt + tree_cnt);
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
for (; remained > 0; remained--) {
|
||||
if (list_empty(&sbi->extent_list))
|
||||
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);
|
||||
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);
|
||||
|
||||
unlock_out:
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
out:
|
||||
trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
|
||||
|
||||
return node_cnt + tree_cnt;
|
||||
}
|
||||
|
||||
unsigned int f2fs_destroy_extent_node(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
unsigned int node_cnt = 0;
|
||||
|
||||
if (!et || !atomic_read(&et->node_cnt))
|
||||
return 0;
|
||||
|
||||
write_lock(&et->lock);
|
||||
node_cnt = __free_extent_tree(sbi, et);
|
||||
write_unlock(&et->lock);
|
||||
|
||||
return node_cnt;
|
||||
}
|
||||
|
||||
void f2fs_drop_extent_tree(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
|
||||
set_inode_flag(inode, FI_NO_EXTENT);
|
||||
|
||||
write_lock(&et->lock);
|
||||
__free_extent_tree(sbi, et);
|
||||
__drop_largest_extent(inode, 0, UINT_MAX);
|
||||
write_unlock(&et->lock);
|
||||
}
|
||||
|
||||
void f2fs_destroy_extent_tree(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
unsigned int node_cnt = 0;
|
||||
|
||||
if (!et)
|
||||
return;
|
||||
|
||||
if (inode->i_nlink && !is_bad_inode(inode) &&
|
||||
atomic_read(&et->node_cnt)) {
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
list_add_tail(&et->list, &sbi->zombie_list);
|
||||
atomic_inc(&sbi->total_zombie_tree);
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* free all extent info belong to this extent tree */
|
||||
node_cnt = f2fs_destroy_extent_node(inode);
|
||||
|
||||
/* delete extent tree entry in radix tree */
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
|
||||
kmem_cache_free(extent_tree_slab, et);
|
||||
atomic_dec(&sbi->total_ext_tree);
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
F2FS_I(inode)->extent_tree = NULL;
|
||||
|
||||
trace_f2fs_destroy_extent_tree(inode, node_cnt);
|
||||
}
|
||||
|
||||
bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
|
||||
struct extent_info *ei)
|
||||
{
|
||||
if (!f2fs_may_extent_tree(inode))
|
||||
return false;
|
||||
|
||||
return f2fs_lookup_extent_tree(inode, pgofs, ei);
|
||||
}
|
||||
|
||||
void f2fs_update_extent_cache(struct dnode_of_data *dn)
|
||||
{
|
||||
pgoff_t fofs;
|
||||
block_t blkaddr;
|
||||
|
||||
if (!f2fs_may_extent_tree(dn->inode))
|
||||
return;
|
||||
|
||||
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;
|
||||
f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1);
|
||||
}
|
||||
|
||||
void f2fs_update_extent_cache_range(struct dnode_of_data *dn,
|
||||
pgoff_t fofs, block_t blkaddr, unsigned int len)
|
||||
|
||||
{
|
||||
if (!f2fs_may_extent_tree(dn->inode))
|
||||
return;
|
||||
|
||||
f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, len);
|
||||
}
|
||||
|
||||
void init_extent_cache_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO);
|
||||
init_rwsem(&sbi->extent_tree_lock);
|
||||
INIT_LIST_HEAD(&sbi->extent_list);
|
||||
spin_lock_init(&sbi->extent_lock);
|
||||
atomic_set(&sbi->total_ext_tree, 0);
|
||||
INIT_LIST_HEAD(&sbi->zombie_list);
|
||||
atomic_set(&sbi->total_zombie_tree, 0);
|
||||
atomic_set(&sbi->total_ext_node, 0);
|
||||
}
|
||||
|
||||
int __init create_extent_cache(void)
|
||||
{
|
||||
extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree",
|
||||
sizeof(struct extent_tree));
|
||||
if (!extent_tree_slab)
|
||||
return -ENOMEM;
|
||||
extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node",
|
||||
sizeof(struct extent_node));
|
||||
if (!extent_node_slab) {
|
||||
kmem_cache_destroy(extent_tree_slab);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_extent_cache(void)
|
||||
{
|
||||
kmem_cache_destroy(extent_node_slab);
|
||||
kmem_cache_destroy(extent_tree_slab);
|
||||
}
|
1481
fs/f2fs/f2fs.h
1481
fs/f2fs/f2fs.h
File diff suppressed because it is too large
Load diff
1837
fs/f2fs/file.c
1837
fs/f2fs/file.c
File diff suppressed because it is too large
Load diff
573
fs/f2fs/gc.c
573
fs/f2fs/gc.c
|
@ -16,7 +16,6 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
@ -24,8 +23,6 @@
|
|||
#include "gc.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static struct kmem_cache *winode_slab;
|
||||
|
||||
static int gc_thread_func(void *data)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = data;
|
||||
|
@ -46,7 +43,7 @@ static int gc_thread_func(void *data)
|
|||
break;
|
||||
|
||||
if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) {
|
||||
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -67,22 +64,25 @@ static int gc_thread_func(void *data)
|
|||
continue;
|
||||
|
||||
if (!is_idle(sbi)) {
|
||||
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (has_enough_invalid_blocks(sbi))
|
||||
wait_ms = decrease_sleep_time(gc_th, wait_ms);
|
||||
decrease_sleep_time(gc_th, &wait_ms);
|
||||
else
|
||||
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
|
||||
stat_inc_bggc_count(sbi);
|
||||
|
||||
/* if return value is not zero, no victim was selected */
|
||||
if (f2fs_gc(sbi))
|
||||
if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC)))
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
|
||||
trace_f2fs_background_gc(sbi->sb, wait_ms,
|
||||
prefree_segments(sbi), free_segments(sbi));
|
||||
|
||||
/* balancing f2fs's metadata periodically */
|
||||
f2fs_balance_fs_bg(sbi);
|
||||
|
||||
|
@ -96,9 +96,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi)
|
|||
dev_t dev = sbi->sb->s_bdev->bd_dev;
|
||||
int err = 0;
|
||||
|
||||
if (!test_opt(sbi, BG_GC))
|
||||
goto out;
|
||||
gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
|
||||
gc_th = f2fs_kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
|
||||
if (!gc_th) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
|
@ -174,9 +172,9 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
/* SSR allocates in a segment unit */
|
||||
if (p->alloc_mode == SSR)
|
||||
return 1 << sbi->log_blocks_per_seg;
|
||||
return sbi->blocks_per_seg;
|
||||
if (p->gc_mode == GC_GREEDY)
|
||||
return (1 << sbi->log_blocks_per_seg) * p->ofs_unit;
|
||||
return sbi->blocks_per_seg * p->ofs_unit;
|
||||
else if (p->gc_mode == GC_CB)
|
||||
return UINT_MAX;
|
||||
else /* No other gc_mode */
|
||||
|
@ -247,6 +245,18 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
|
|||
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.
|
||||
* One is garbage collection and the other is SSR segment selection.
|
||||
|
@ -260,8 +270,9 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
struct victim_sel_policy p;
|
||||
unsigned int secno, max_cost;
|
||||
int nsearched = 0;
|
||||
unsigned int secno, max_cost, last_victim;
|
||||
unsigned int last_segment = MAIN_SEGS(sbi);
|
||||
unsigned int nsearched = 0;
|
||||
|
||||
mutex_lock(&dirty_i->seglist_lock);
|
||||
|
||||
|
@ -271,6 +282,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
p.min_segno = NULL_SEGNO;
|
||||
p.min_cost = max_cost = get_max_cost(sbi, &p);
|
||||
|
||||
if (p.max_search == 0)
|
||||
goto out;
|
||||
|
||||
last_victim = sbi->last_victim[p.gc_mode];
|
||||
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
||||
p.min_segno = check_bg_victims(sbi);
|
||||
if (p.min_segno != NULL_SEGNO)
|
||||
|
@ -281,9 +296,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
unsigned long cost;
|
||||
unsigned int segno;
|
||||
|
||||
segno = find_next_bit(p.dirty_segmap, MAIN_SEGS(sbi), p.offset);
|
||||
if (segno >= MAIN_SEGS(sbi)) {
|
||||
segno = find_next_bit(p.dirty_segmap, last_segment, p.offset);
|
||||
if (segno >= last_segment) {
|
||||
if (sbi->last_victim[p.gc_mode]) {
|
||||
last_segment = sbi->last_victim[p.gc_mode];
|
||||
sbi->last_victim[p.gc_mode] = 0;
|
||||
p.offset = 0;
|
||||
continue;
|
||||
|
@ -292,27 +308,35 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
p.offset = segno + p.ofs_unit;
|
||||
if (p.ofs_unit > 1)
|
||||
if (p.ofs_unit > 1) {
|
||||
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);
|
||||
|
||||
if (sec_usage_check(sbi, secno))
|
||||
continue;
|
||||
goto next;
|
||||
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
cost = get_gc_cost(sbi, segno, &p);
|
||||
|
||||
if (p.min_cost > cost) {
|
||||
p.min_segno = segno;
|
||||
p.min_cost = cost;
|
||||
} else if (unlikely(cost == max_cost)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsearched++ >= p.max_search) {
|
||||
sbi->last_victim[p.gc_mode] = segno;
|
||||
next:
|
||||
if (nsearched >= p.max_search) {
|
||||
if (!sbi->last_victim[p.gc_mode] && segno <= last_victim)
|
||||
sbi->last_victim[p.gc_mode] = last_victim + 1;
|
||||
else
|
||||
sbi->last_victim[p.gc_mode] = segno + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -331,6 +355,7 @@ got_it:
|
|||
sbi->cur_victim_sec,
|
||||
prefree_segments(sbi), free_segments(sbi));
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&dirty_i->seglist_lock);
|
||||
|
||||
return (p.min_segno == NULL_SEGNO) ? 0 : 1;
|
||||
|
@ -340,37 +365,39 @@ static const struct victim_selection default_v_ops = {
|
|||
.get_victim = get_victim_by_default,
|
||||
};
|
||||
|
||||
static struct inode *find_gc_inode(nid_t ino, struct list_head *ilist)
|
||||
static struct inode *find_gc_inode(struct gc_inode_list *gc_list, nid_t ino)
|
||||
{
|
||||
struct inode_entry *ie;
|
||||
|
||||
list_for_each_entry(ie, ilist, list)
|
||||
if (ie->inode->i_ino == ino)
|
||||
return ie->inode;
|
||||
ie = radix_tree_lookup(&gc_list->iroot, ino);
|
||||
if (ie)
|
||||
return ie->inode;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_gc_inode(struct inode *inode, struct list_head *ilist)
|
||||
static void add_gc_inode(struct gc_inode_list *gc_list, struct inode *inode)
|
||||
{
|
||||
struct inode_entry *new_ie;
|
||||
|
||||
if (inode == find_gc_inode(inode->i_ino, ilist)) {
|
||||
if (inode == find_gc_inode(gc_list, inode->i_ino)) {
|
||||
iput(inode);
|
||||
return;
|
||||
}
|
||||
|
||||
new_ie = f2fs_kmem_cache_alloc(winode_slab, GFP_NOFS);
|
||||
new_ie = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
|
||||
new_ie->inode = inode;
|
||||
list_add_tail(&new_ie->list, ilist);
|
||||
|
||||
f2fs_radix_tree_insert(&gc_list->iroot, inode->i_ino, new_ie);
|
||||
list_add_tail(&new_ie->list, &gc_list->ilist);
|
||||
}
|
||||
|
||||
static void put_gc_inode(struct list_head *ilist)
|
||||
static void put_gc_inode(struct gc_inode_list *gc_list)
|
||||
{
|
||||
struct inode_entry *ie, *next_ie;
|
||||
list_for_each_entry_safe(ie, next_ie, ilist, list) {
|
||||
list_for_each_entry_safe(ie, next_ie, &gc_list->ilist, list) {
|
||||
radix_tree_delete(&gc_list->iroot, ie->inode->i_ino);
|
||||
iput(ie->inode);
|
||||
list_del(&ie->list);
|
||||
kmem_cache_free(winode_slab, ie);
|
||||
kmem_cache_free(inode_entry_slab, ie);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,14 +425,18 @@ static void gc_node_segment(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
bool initial = true;
|
||||
struct f2fs_summary *entry;
|
||||
block_t start_addr;
|
||||
int off;
|
||||
|
||||
start_addr = START_BLOCK(sbi, segno);
|
||||
|
||||
next_step:
|
||||
entry = sum;
|
||||
|
||||
for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
|
||||
nid_t nid = le32_to_cpu(entry->nid);
|
||||
struct page *node_page;
|
||||
struct node_info ni;
|
||||
|
||||
/* stop BG_GC if there is not enough free sections. */
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
||||
|
@ -428,38 +459,20 @@ next_step:
|
|||
continue;
|
||||
}
|
||||
|
||||
/* set page dirty and write it */
|
||||
if (gc_type == FG_GC) {
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
set_page_dirty(node_page);
|
||||
} else {
|
||||
if (!PageWriteback(node_page))
|
||||
set_page_dirty(node_page);
|
||||
get_node_info(sbi, nid, &ni);
|
||||
if (ni.blk_addr != start_addr + off) {
|
||||
f2fs_put_page(node_page, 1);
|
||||
continue;
|
||||
}
|
||||
f2fs_put_page(node_page, 1);
|
||||
stat_inc_node_blk_count(sbi, 1);
|
||||
|
||||
move_node_page(node_page, gc_type);
|
||||
stat_inc_node_blk_count(sbi, 1, gc_type);
|
||||
}
|
||||
|
||||
if (initial) {
|
||||
initial = false;
|
||||
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);
|
||||
|
||||
/*
|
||||
* In the case of FG_GC, it'd be better to reclaim this victim
|
||||
* completely.
|
||||
*/
|
||||
if (get_valid_blocks(sbi, segno, 1) != 0)
|
||||
goto next_step;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -469,7 +482,7 @@ next_step:
|
|||
* as indirect or double indirect node blocks, are given, it must be a caller's
|
||||
* 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 bidx;
|
||||
|
@ -486,10 +499,10 @@ 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);
|
||||
bidx = node_ofs - 5 - dec;
|
||||
}
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi);
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode);
|
||||
}
|
||||
|
||||
static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
struct node_info *dni, block_t blkaddr, unsigned int *nofs)
|
||||
{
|
||||
struct page *node_page;
|
||||
|
@ -502,13 +515,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
|
||||
node_page = get_node_page(sbi, nid);
|
||||
if (IS_ERR(node_page))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
get_node_info(sbi, nid, dni);
|
||||
|
||||
if (sum->version != dni->version) {
|
||||
f2fs_put_page(node_page, 1);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
*nofs = ofs_of_node(node_page);
|
||||
|
@ -516,16 +529,116 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
f2fs_put_page(node_page, 1);
|
||||
|
||||
if (source_blkaddr != blkaddr)
|
||||
return 0;
|
||||
return 1;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
||||
static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
{
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC,
|
||||
.rw = READ_SYNC,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
struct page *page;
|
||||
block_t newaddr;
|
||||
int err;
|
||||
|
||||
/* do not read out */
|
||||
page = f2fs_grab_cache_page(inode->i_mapping, bidx, false);
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (unlikely(dn.data_blkaddr == NULL_ADDR)) {
|
||||
ClearPageUptodate(page);
|
||||
goto put_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* don't cache encrypted data into meta inode until previous dirty
|
||||
* data were writebacked to avoid racing between GC and flush.
|
||||
*/
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
|
||||
get_node_info(fio.sbi, dn.nid, &ni);
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* read page */
|
||||
fio.page = page;
|
||||
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
||||
|
||||
allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
|
||||
&sum, CURSEG_COLD_DATA);
|
||||
|
||||
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
|
||||
FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
||||
if (!fio.encrypted_page) {
|
||||
err = -ENOMEM;
|
||||
goto recover_block;
|
||||
}
|
||||
|
||||
err = f2fs_submit_page_bio(&fio);
|
||||
if (err)
|
||||
goto put_page_out;
|
||||
|
||||
/* write page */
|
||||
lock_page(fio.encrypted_page);
|
||||
|
||||
if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) {
|
||||
err = -EIO;
|
||||
goto put_page_out;
|
||||
}
|
||||
if (unlikely(!PageUptodate(fio.encrypted_page))) {
|
||||
err = -EIO;
|
||||
goto put_page_out;
|
||||
}
|
||||
|
||||
set_page_dirty(fio.encrypted_page);
|
||||
f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true);
|
||||
if (clear_page_dirty_for_io(fio.encrypted_page))
|
||||
dec_page_count(fio.sbi, F2FS_DIRTY_META);
|
||||
|
||||
set_page_writeback(fio.encrypted_page);
|
||||
|
||||
/* allocate block address */
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||
|
||||
fio.rw = WRITE_SYNC;
|
||||
fio.new_blkaddr = newaddr;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
|
||||
f2fs_update_data_blkaddr(&dn, newaddr);
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
if (page->index == 0)
|
||||
set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
|
||||
put_page_out:
|
||||
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:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = get_lock_data_page(inode, bidx, true);
|
||||
if (IS_ERR(page))
|
||||
return;
|
||||
|
||||
if (gc_type == BG_GC) {
|
||||
if (PageWriteback(page))
|
||||
|
@ -533,12 +646,30 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
|||
set_page_dirty(page);
|
||||
set_cold_data(page);
|
||||
} else {
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
bool is_dirty = PageDirty(page);
|
||||
int err;
|
||||
|
||||
retry:
|
||||
set_page_dirty(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (clear_page_dirty_for_io(page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
|
||||
set_cold_data(page);
|
||||
do_write_data_page(page, &fio);
|
||||
|
||||
err = do_write_data_page(&fio);
|
||||
if (err == -ENOMEM && is_dirty) {
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
clear_cold_data(page);
|
||||
}
|
||||
out:
|
||||
|
@ -553,7 +684,7 @@ out:
|
|||
* the victim data block is ignored.
|
||||
*/
|
||||
static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
struct list_head *ilist, 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 f2fs_summary *entry;
|
||||
|
@ -586,7 +717,7 @@ next_step:
|
|||
}
|
||||
|
||||
/* Get an inode by ino with checking validity */
|
||||
if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0)
|
||||
if (!is_alive(sbi, entry, &dni, start_addr + off, &nofs))
|
||||
continue;
|
||||
|
||||
if (phase == 1) {
|
||||
|
@ -601,141 +732,217 @@ next_step:
|
|||
if (IS_ERR(inode) || is_bad_inode(inode))
|
||||
continue;
|
||||
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||
/* if encrypted inode, let's go phase 3 */
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
add_gc_inode(gc_list, inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
data_page = find_data_page(inode,
|
||||
start_bidx + ofs_in_node, false);
|
||||
if (IS_ERR(data_page))
|
||||
goto next_iput;
|
||||
start_bidx = start_bidx_of_node(nofs, inode);
|
||||
data_page = get_read_data_page(inode,
|
||||
start_bidx + ofs_in_node, READA, true);
|
||||
if (IS_ERR(data_page)) {
|
||||
iput(inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
f2fs_put_page(data_page, 0);
|
||||
add_gc_inode(inode, ilist);
|
||||
} else {
|
||||
inode = find_gc_inode(dni.ino, ilist);
|
||||
if (inode) {
|
||||
start_bidx = start_bidx_of_node(nofs,
|
||||
F2FS_I(inode));
|
||||
data_page = get_lock_data_page(inode,
|
||||
start_bidx + ofs_in_node);
|
||||
if (IS_ERR(data_page))
|
||||
continue;
|
||||
move_data_page(inode, data_page, gc_type);
|
||||
stat_inc_data_blk_count(sbi, 1);
|
||||
}
|
||||
add_gc_inode(gc_list, inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* phase 3 */
|
||||
inode = find_gc_inode(gc_list, dni.ino);
|
||||
if (inode) {
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
bool locked = false;
|
||||
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
if (!down_write_trylock(&fi->dio_rwsem[READ]))
|
||||
continue;
|
||||
if (!down_write_trylock(
|
||||
&fi->dio_rwsem[WRITE])) {
|
||||
up_write(&fi->dio_rwsem[READ]);
|
||||
continue;
|
||||
}
|
||||
locked = true;
|
||||
}
|
||||
|
||||
start_bidx = start_bidx_of_node(nofs, inode)
|
||||
+ ofs_in_node;
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
move_encrypted_block(inode, start_bidx);
|
||||
else
|
||||
move_data_page(inode, start_bidx, gc_type);
|
||||
|
||||
if (locked) {
|
||||
up_write(&fi->dio_rwsem[WRITE]);
|
||||
up_write(&fi->dio_rwsem[READ]);
|
||||
}
|
||||
|
||||
stat_inc_data_blk_count(sbi, 1, gc_type);
|
||||
}
|
||||
continue;
|
||||
next_iput:
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
if (++phase < 4)
|
||||
goto next_step;
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
|
||||
/*
|
||||
* In the case of FG_GC, it'd be better to reclaim this victim
|
||||
* completely.
|
||||
*/
|
||||
if (get_valid_blocks(sbi, segno, 1) != 0) {
|
||||
phase = 2;
|
||||
goto next_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
|
||||
int gc_type, int type)
|
||||
int gc_type)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sit_i->sentry_lock);
|
||||
ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, type, LFS);
|
||||
ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type,
|
||||
NO_CHECK_TYPE, LFS);
|
||||
mutex_unlock(&sit_i->sentry_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
struct list_head *ilist, int gc_type)
|
||||
static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
unsigned int start_segno,
|
||||
struct gc_inode_list *gc_list, int gc_type)
|
||||
{
|
||||
struct page *sum_page;
|
||||
struct f2fs_summary_block *sum;
|
||||
struct blk_plug plug;
|
||||
|
||||
/* read segment summary of victim */
|
||||
sum_page = get_sum_page(sbi, segno);
|
||||
|
||||
blk_start_plug(&plug);
|
||||
|
||||
sum = page_address(sum_page);
|
||||
|
||||
switch (GET_SUM_TYPE((&sum->footer))) {
|
||||
case SUM_TYPE_NODE:
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
break;
|
||||
case SUM_TYPE_DATA:
|
||||
gc_data_segment(sbi, sum->entries, ilist, segno, gc_type);
|
||||
break;
|
||||
}
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)));
|
||||
stat_inc_call_count(sbi->stat_info);
|
||||
|
||||
f2fs_put_page(sum_page, 1);
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct list_head ilist;
|
||||
unsigned int segno, i;
|
||||
int gc_type = BG_GC;
|
||||
int nfree = 0;
|
||||
int ret = -1;
|
||||
struct cp_control cpc = {
|
||||
.reason = CP_SYNC,
|
||||
};
|
||||
|
||||
INIT_LIST_HEAD(&ilist);
|
||||
gc_more:
|
||||
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
|
||||
goto stop;
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto stop;
|
||||
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) {
|
||||
gc_type = FG_GC;
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
|
||||
if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
|
||||
goto stop;
|
||||
ret = 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;
|
||||
|
||||
/* readahead multi ssa blocks those have contiguous address */
|
||||
if (sbi->segs_per_sec > 1)
|
||||
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec,
|
||||
META_SSA);
|
||||
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++)
|
||||
do_garbage_collect(sbi, segno + i, &ilist, gc_type);
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
nfree++;
|
||||
WARN_ON(get_valid_blocks(sbi, segno, sbi->segs_per_sec));
|
||||
/* reference all summary page */
|
||||
while (segno < end_segno) {
|
||||
sum_page = get_sum_page(sbi, segno++);
|
||||
unlock_page(sum_page);
|
||||
}
|
||||
|
||||
if (has_not_enough_free_secs(sbi, nfree))
|
||||
goto gc_more;
|
||||
blk_start_plug(&plug);
|
||||
|
||||
for (segno = start_segno; segno < end_segno; segno++) {
|
||||
|
||||
if (get_valid_blocks(sbi, segno, 1) == 0)
|
||||
continue;
|
||||
|
||||
/* 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);
|
||||
f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer)));
|
||||
|
||||
/*
|
||||
* this is to avoid deadlock:
|
||||
* - lock_page(sum_page) - f2fs_replace_block
|
||||
* - check_valid_map() - mutex_lock(sentry_lock)
|
||||
* - mutex_lock(sentry_lock) - change_curseg()
|
||||
* - lock_page(sum_page)
|
||||
*/
|
||||
|
||||
if (type == SUM_TYPE_NODE)
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
else
|
||||
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)
|
||||
write_checkpoint(sbi, &cpc);
|
||||
f2fs_submit_merged_bio(sbi,
|
||||
(type == SUM_TYPE_NODE) ? NODE : DATA, WRITE);
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
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);
|
||||
|
||||
return seg_freed;
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
|
||||
{
|
||||
unsigned int segno;
|
||||
int gc_type = sync ? FG_GC : BG_GC;
|
||||
int sec_freed = 0, seg_freed;
|
||||
int ret = -EINVAL;
|
||||
struct cp_control cpc;
|
||||
struct gc_inode_list gc_list = {
|
||||
.ilist = LIST_HEAD_INIT(gc_list.ilist),
|
||||
.iroot = RADIX_TREE_INIT(GFP_NOFS),
|
||||
};
|
||||
|
||||
cpc.reason = __get_cp_reason(sbi);
|
||||
gc_more:
|
||||
segno = NULL_SEGNO;
|
||||
|
||||
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
|
||||
goto stop;
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
ret = -EIO;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) {
|
||||
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)) {
|
||||
write_checkpoint(sbi, &cpc);
|
||||
segno = NULL_SEGNO;
|
||||
} else if (has_not_enough_free_secs(sbi, 0)) {
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
}
|
||||
|
||||
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
|
||||
goto stop;
|
||||
ret = 0;
|
||||
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
||||
|
||||
if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
|
||||
sec_freed++;
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
|
||||
if (!sync) {
|
||||
if (has_not_enough_free_secs(sbi, sec_freed))
|
||||
goto gc_more;
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
stop:
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
|
||||
put_gc_inode(&ilist);
|
||||
put_gc_inode(&gc_list);
|
||||
|
||||
if (sync)
|
||||
ret = sec_freed ? 0 : -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -743,17 +950,3 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
|
|||
{
|
||||
DIRTY_I(sbi)->v_ops = &default_v_ops;
|
||||
}
|
||||
|
||||
int __init create_gc_caches(void)
|
||||
{
|
||||
winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes",
|
||||
sizeof(struct inode_entry));
|
||||
if (!winode_slab)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_gc_caches(void)
|
||||
{
|
||||
kmem_cache_destroy(winode_slab);
|
||||
}
|
||||
|
|
42
fs/f2fs/gc.h
42
fs/f2fs/gc.h
|
@ -35,9 +35,9 @@ struct f2fs_gc_kthread {
|
|||
unsigned int gc_idle;
|
||||
};
|
||||
|
||||
struct inode_entry {
|
||||
struct list_head list;
|
||||
struct inode *inode;
|
||||
struct gc_inode_list {
|
||||
struct list_head ilist;
|
||||
struct radix_tree_root iroot;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -64,26 +64,26 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
|
|||
return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100;
|
||||
}
|
||||
|
||||
static inline long increase_sleep_time(struct f2fs_gc_kthread *gc_th, long wait)
|
||||
static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
long *wait)
|
||||
{
|
||||
if (wait == gc_th->no_gc_sleep_time)
|
||||
return wait;
|
||||
if (*wait == gc_th->no_gc_sleep_time)
|
||||
return;
|
||||
|
||||
wait += gc_th->min_sleep_time;
|
||||
if (wait > gc_th->max_sleep_time)
|
||||
wait = gc_th->max_sleep_time;
|
||||
return wait;
|
||||
*wait += gc_th->min_sleep_time;
|
||||
if (*wait > gc_th->max_sleep_time)
|
||||
*wait = gc_th->max_sleep_time;
|
||||
}
|
||||
|
||||
static inline long decrease_sleep_time(struct f2fs_gc_kthread *gc_th, long wait)
|
||||
static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
long *wait)
|
||||
{
|
||||
if (wait == gc_th->no_gc_sleep_time)
|
||||
wait = gc_th->max_sleep_time;
|
||||
if (*wait == gc_th->no_gc_sleep_time)
|
||||
*wait = gc_th->max_sleep_time;
|
||||
|
||||
wait -= gc_th->min_sleep_time;
|
||||
if (wait <= gc_th->min_sleep_time)
|
||||
wait = gc_th->min_sleep_time;
|
||||
return wait;
|
||||
*wait -= gc_th->min_sleep_time;
|
||||
if (*wait <= gc_th->min_sleep_time)
|
||||
*wait = gc_th->min_sleep_time;
|
||||
}
|
||||
|
||||
static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
||||
|
@ -100,11 +100,3 @@ static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
|||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int is_idle(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct block_device *bdev = sbi->sb->s_bdev;
|
||||
struct request_queue *q = bdev_get_queue(bdev);
|
||||
struct request_list *rl = &q->root_rl;
|
||||
return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]);
|
||||
}
|
||||
|
|
|
@ -79,8 +79,7 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
|
|||
const unsigned char *name = name_info->name;
|
||||
size_t len = name_info->len;
|
||||
|
||||
if ((len <= 2) && (name[0] == '.') &&
|
||||
(name[1] == '.' || name[1] == '\0'))
|
||||
if (is_dot_dotdot(name_info))
|
||||
return 0;
|
||||
|
||||
/* Initialize the default seed for the hash checksum functions */
|
||||
|
|
678
fs/f2fs/inline.c
678
fs/f2fs/inline.c
|
@ -12,38 +12,75 @@
|
|||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
||||
bool f2fs_may_inline(struct inode *inode)
|
||||
bool f2fs_may_inline_data(struct inode *inode)
|
||||
{
|
||||
block_t nr_blocks;
|
||||
loff_t i_size;
|
||||
|
||||
if (!test_opt(F2FS_I_SB(inode), INLINE_DATA))
|
||||
return false;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
return false;
|
||||
|
||||
nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2;
|
||||
if (inode->i_blocks > nr_blocks)
|
||||
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))
|
||||
return false;
|
||||
|
||||
i_size = i_size_read(inode);
|
||||
if (i_size > MAX_INLINE_DATA)
|
||||
if (i_size_read(inode) > MAX_INLINE_DATA)
|
||||
return false;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool f2fs_may_inline_dentry(struct inode *inode)
|
||||
{
|
||||
if (!test_opt(F2FS_I_SB(inode), INLINE_DENTRY))
|
||||
return false;
|
||||
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void read_inline_data(struct page *page, struct page *ipage)
|
||||
{
|
||||
void *src_addr, *dst_addr;
|
||||
|
||||
if (PageUptodate(page))
|
||||
return;
|
||||
|
||||
f2fs_bug_on(F2FS_P_SB(page), page->index);
|
||||
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE);
|
||||
|
||||
/* Copy the whole inline data block */
|
||||
src_addr = inline_data_addr(ipage);
|
||||
dst_addr = kmap_atomic(page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(dst_addr);
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
|
||||
bool truncate_inline_inode(struct page *ipage, u64 from)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
if (from >= MAX_INLINE_DATA)
|
||||
return false;
|
||||
|
||||
addr = inline_data_addr(ipage);
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
memset(addr + from, 0, MAX_INLINE_DATA - from);
|
||||
set_page_dirty(ipage);
|
||||
return true;
|
||||
}
|
||||
|
||||
int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct page *ipage;
|
||||
void *src_addr, *dst_addr;
|
||||
|
||||
if (page->index) {
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
|
@ -51,112 +88,111 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
|||
return PTR_ERR(ipage);
|
||||
}
|
||||
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
|
||||
if (!f2fs_has_inline_data(inode)) {
|
||||
f2fs_put_page(ipage, 1);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Copy the whole inline data block */
|
||||
src_addr = inline_data_addr(ipage);
|
||||
dst_addr = kmap(page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
kunmap(page);
|
||||
if (page->index)
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
else
|
||||
read_inline_data(page, ipage);
|
||||
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
||||
out:
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __f2fs_convert_inline_data(struct inode *inode, struct page *page)
|
||||
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
{
|
||||
int err = 0;
|
||||
struct page *ipage;
|
||||
struct dnode_of_data dn;
|
||||
void *src_addr, *dst_addr;
|
||||
block_t new_blk_addr;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(dn->inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC | REQ_PRIO,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
int dirty, err;
|
||||
|
||||
if (!f2fs_exist_data(dn->inode))
|
||||
goto clear_out;
|
||||
|
||||
err = f2fs_reserve_block(dn, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page));
|
||||
|
||||
read_inline_data(page, dn->inode_page);
|
||||
set_page_dirty(page);
|
||||
|
||||
/* clear dirty state */
|
||||
dirty = clear_page_dirty_for_io(page);
|
||||
|
||||
/* write data page to try to make data consistent */
|
||||
set_page_writeback(page);
|
||||
fio.old_blkaddr = dn->data_blkaddr;
|
||||
write_data_page(dn, &fio);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (dirty)
|
||||
inode_dec_dirty_pages(dn->inode);
|
||||
|
||||
/* this converted inline_data should be recovered. */
|
||||
set_inode_flag(dn->inode, FI_APPEND_WRITE);
|
||||
|
||||
/* clear inline data and flag after data writeback */
|
||||
truncate_inline_inode(dn->inode_page, 0);
|
||||
clear_inline_node(dn->inode_page);
|
||||
clear_out:
|
||||
stat_dec_inline_inode(dn->inode);
|
||||
f2fs_clear_inline_inode(dn->inode);
|
||||
f2fs_put_dnode(dn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_convert_inline_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct dnode_of_data dn;
|
||||
struct page *ipage, *page;
|
||||
int err = 0;
|
||||
|
||||
if (!f2fs_has_inline_data(inode))
|
||||
return 0;
|
||||
|
||||
page = f2fs_grab_cache_page(inode->i_mapping, 0, false);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
err = PTR_ERR(ipage);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* someone else converted inline_data already */
|
||||
if (!f2fs_has_inline_data(inode))
|
||||
goto out;
|
||||
set_new_dnode(&dn, inode, ipage, ipage, 0);
|
||||
|
||||
/*
|
||||
* i_addr[0] is not used for inline data,
|
||||
* so reserving new block will not destroy inline data
|
||||
*/
|
||||
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
||||
err = f2fs_reserve_block(&dn, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
if (f2fs_has_inline_data(inode))
|
||||
err = f2fs_convert_inline_page(&dn, page);
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
|
||||
|
||||
/* Copy the whole inline data block */
|
||||
src_addr = inline_data_addr(ipage);
|
||||
dst_addr = kmap(page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
kunmap(page);
|
||||
SetPageUptodate(page);
|
||||
|
||||
/* write data page to try to make data consistent */
|
||||
set_page_writeback(page);
|
||||
write_data_page(page, &dn, &new_blk_addr, &fio);
|
||||
update_extent_cache(new_blk_addr, &dn);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
|
||||
/* clear inline data and flag after data writeback */
|
||||
zero_user_segment(ipage, INLINE_DATA_OFFSET,
|
||||
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
|
||||
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
stat_dec_inline_inode(inode);
|
||||
|
||||
sync_inode_page(&dn);
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size,
|
||||
struct page *page)
|
||||
{
|
||||
struct page *new_page = page;
|
||||
int err;
|
||||
|
||||
if (!f2fs_has_inline_data(inode))
|
||||
return 0;
|
||||
else if (to_size <= MAX_INLINE_DATA)
|
||||
return 0;
|
||||
|
||||
if (!page || page->index != 0) {
|
||||
new_page = grab_cache_page(inode->i_mapping, 0);
|
||||
if (!new_page)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = __f2fs_convert_inline_data(inode, new_page);
|
||||
if (!page || page->index != 0)
|
||||
f2fs_put_page(new_page, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
int f2fs_write_inline_data(struct inode *inode,
|
||||
struct page *page, unsigned size)
|
||||
int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
{
|
||||
void *src_addr, *dst_addr;
|
||||
struct page *ipage;
|
||||
struct dnode_of_data dn;
|
||||
int err;
|
||||
|
||||
|
@ -164,49 +200,29 @@ int f2fs_write_inline_data(struct inode *inode,
|
|||
err = get_dnode_of_data(&dn, 0, LOOKUP_NODE);
|
||||
if (err)
|
||||
return err;
|
||||
ipage = dn.inode_page;
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
zero_user_segment(ipage, INLINE_DATA_OFFSET,
|
||||
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
|
||||
src_addr = kmap(page);
|
||||
dst_addr = inline_data_addr(ipage);
|
||||
memcpy(dst_addr, src_addr, size);
|
||||
kunmap(page);
|
||||
|
||||
/* Release the first data block if it is allocated */
|
||||
if (!f2fs_has_inline_data(inode)) {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
stat_inc_inline_inode(inode);
|
||||
f2fs_put_dnode(&dn);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||
sync_inode_page(&dn);
|
||||
f2fs_bug_on(F2FS_I_SB(inode), page->index);
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true);
|
||||
src_addr = kmap_atomic(page);
|
||||
dst_addr = inline_data_addr(dn.inode_page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
kunmap_atomic(src_addr);
|
||||
set_page_dirty(dn.inode_page);
|
||||
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
|
||||
clear_inline_node(dn.inode_page);
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void truncate_inline_data(struct inode *inode, u64 from)
|
||||
{
|
||||
struct page *ipage;
|
||||
|
||||
if (from >= MAX_INLINE_DATA)
|
||||
return;
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return;
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
|
||||
zero_user_segment(ipage, INLINE_DATA_OFFSET + from,
|
||||
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
|
||||
set_page_dirty(ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
}
|
||||
|
||||
bool recover_inline_data(struct inode *inode, struct page *npage)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
|
@ -231,12 +247,16 @@ process_inline:
|
|||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
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);
|
||||
dst_addr = inline_data_addr(ipage);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
update_inode(inode, ipage);
|
||||
|
||||
set_inode_flag(inode, FI_INLINE_DATA);
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
return true;
|
||||
}
|
||||
|
@ -244,16 +264,398 @@ process_inline:
|
|||
if (f2fs_has_inline_data(inode)) {
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
f2fs_bug_on(sbi, IS_ERR(ipage));
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
zero_user_segment(ipage, INLINE_DATA_OFFSET,
|
||||
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
|
||||
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
update_inode(inode, ipage);
|
||||
if (!truncate_inline_inode(ipage, 0))
|
||||
return false;
|
||||
f2fs_clear_inline_inode(inode);
|
||||
f2fs_put_page(ipage, 1);
|
||||
} else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) {
|
||||
truncate_blocks(inode, 0, false);
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
if (truncate_blocks(inode, 0, false))
|
||||
return false;
|
||||
goto process_inline;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
struct fscrypt_name *fname, struct page **res_page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct page *ipage;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
*res_page = ipage;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
||||
de = find_target_dentry(fname, namehash, NULL, &d);
|
||||
unlock_page(ipage);
|
||||
if (de)
|
||||
*res_page = ipage;
|
||||
else
|
||||
f2fs_put_page(ipage, 0);
|
||||
|
||||
return de;
|
||||
}
|
||||
|
||||
int make_empty_inline_dir(struct inode *inode, struct inode *parent,
|
||||
struct page *ipage)
|
||||
{
|
||||
struct f2fs_inline_dentry *dentry_blk;
|
||||
struct f2fs_dentry_ptr d;
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
do_make_empty_dir(inode, parent, &d);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
|
||||
/* update i_size to MAX_INLINE_DATA */
|
||||
if (i_size_read(inode) < MAX_INLINE_DATA)
|
||||
f2fs_i_size_write(inode, MAX_INLINE_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: ipage is grabbed by caller, but if any error occurs, we should
|
||||
* release ipage in this function.
|
||||
*/
|
||||
static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
{
|
||||
struct page *page;
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
int err;
|
||||
|
||||
page = f2fs_grab_cache_page(dir->i_mapping, 0, false);
|
||||
if (!page) {
|
||||
f2fs_put_page(ipage, 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
set_new_dnode(&dn, dir, ipage, NULL, 0);
|
||||
err = f2fs_reserve_block(&dn, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE);
|
||||
|
||||
dentry_blk = kmap_atomic(page);
|
||||
|
||||
/* copy data from inline dentry block to new dentry block */
|
||||
memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
|
||||
INLINE_DENTRY_BITMAP_SIZE);
|
||||
memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0,
|
||||
SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE);
|
||||
/*
|
||||
* we do not need to zero out remainder part of dentry and filename
|
||||
* field, since we have used bitmap for marking the usage status of
|
||||
* them, besides, we can also ignore copying/zeroing reserved space
|
||||
* of dentry block, because them haven't been used so far.
|
||||
*/
|
||||
memcpy(dentry_blk->dentry, inline_dentry->dentry,
|
||||
sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY);
|
||||
memcpy(dentry_blk->filename, inline_dentry->filename,
|
||||
NR_INLINE_DENTRY * F2FS_SLOT_LEN);
|
||||
|
||||
kunmap_atomic(dentry_blk);
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
set_page_dirty(page);
|
||||
|
||||
/* clear inline dir and flag after data writeback */
|
||||
truncate_inline_inode(ipage, 0);
|
||||
|
||||
stat_dec_inline_dir(dir);
|
||||
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
||||
|
||||
f2fs_i_depth_write(dir, 1);
|
||||
if (i_size_read(dir) < PAGE_SIZE)
|
||||
f2fs_i_size_write(dir, PAGE_SIZE);
|
||||
out:
|
||||
f2fs_put_page(page, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_add_inline_entries(struct inode *dir,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
{
|
||||
struct f2fs_dentry_ptr d;
|
||||
unsigned long bit_pos = 0;
|
||||
int err = 0;
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
||||
|
||||
while (bit_pos < d.max) {
|
||||
struct f2fs_dir_entry *de;
|
||||
struct qstr new_name;
|
||||
nid_t ino;
|
||||
umode_t fake_mode;
|
||||
|
||||
if (!test_bit_le(bit_pos, d.bitmap)) {
|
||||
bit_pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
de = &d.dentry[bit_pos];
|
||||
|
||||
if (unlikely(!de->name_len)) {
|
||||
bit_pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
new_name.name = d.filename[bit_pos];
|
||||
new_name.len = de->name_len;
|
||||
|
||||
ino = le32_to_cpu(de->ino);
|
||||
fake_mode = get_de_type(de) << S_SHIFT;
|
||||
|
||||
err = f2fs_add_regular_entry(dir, &new_name, NULL,
|
||||
ino, fake_mode);
|
||||
if (err)
|
||||
goto punch_dentry_pages;
|
||||
|
||||
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
|
||||
}
|
||||
return 0;
|
||||
punch_dentry_pages:
|
||||
truncate_inode_pages(&dir->i_data, 0);
|
||||
truncate_blocks(dir, 0, false);
|
||||
remove_dirty_inode(dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
{
|
||||
struct f2fs_inline_dentry *backup_dentry;
|
||||
int err;
|
||||
|
||||
backup_dentry = f2fs_kmalloc(sizeof(struct f2fs_inline_dentry),
|
||||
GFP_F2FS_ZERO);
|
||||
if (!backup_dentry) {
|
||||
f2fs_put_page(ipage, 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA);
|
||||
truncate_inline_inode(ipage, 0);
|
||||
|
||||
unlock_page(ipage);
|
||||
|
||||
err = f2fs_add_inline_entries(dir, backup_dentry);
|
||||
if (err)
|
||||
goto recover;
|
||||
|
||||
lock_page(ipage);
|
||||
|
||||
stat_dec_inline_dir(dir);
|
||||
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
||||
kfree(backup_dentry);
|
||||
return 0;
|
||||
recover:
|
||||
lock_page(ipage);
|
||||
memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA);
|
||||
f2fs_i_depth_write(dir, 0);
|
||||
f2fs_i_size_write(dir, MAX_INLINE_DATA);
|
||||
set_page_dirty(ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
||||
kfree(backup_dentry);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
|
||||
struct f2fs_inline_dentry *inline_dentry)
|
||||
{
|
||||
if (!F2FS_I(dir)->i_dir_level)
|
||||
return f2fs_move_inline_dirents(dir, ipage, inline_dentry);
|
||||
else
|
||||
return f2fs_move_rehashed_dirents(dir, ipage, inline_dentry);
|
||||
}
|
||||
|
||||
int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
||||
struct inode *inode, nid_t ino, umode_t mode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct page *ipage;
|
||||
unsigned int bit_pos;
|
||||
f2fs_hash_t name_hash;
|
||||
size_t namelen = name->len;
|
||||
struct f2fs_inline_dentry *dentry_blk = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
int slots = GET_DENTRY_SLOTS(namelen);
|
||||
struct page *page = NULL;
|
||||
int err = 0;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
|
||||
slots, NR_INLINE_DENTRY);
|
||||
if (bit_pos >= NR_INLINE_DENTRY) {
|
||||
err = f2fs_convert_inline_dir(dir, ipage, dentry_blk);
|
||||
if (err)
|
||||
return err;
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (inode) {
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
page = init_inode_metadata(inode, dir, name, ipage);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
name_hash = f2fs_dentry_hash(name);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
|
||||
/* we don't need to mark_inode_dirty now */
|
||||
if (inode) {
|
||||
f2fs_i_pino_write(inode, dir->i_ino);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
update_parent_metadata(dir, inode, 0);
|
||||
fail:
|
||||
if (inode)
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
out:
|
||||
f2fs_put_page(ipage, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
struct inode *dir, struct inode *inode)
|
||||
{
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
|
||||
unsigned int bit_pos;
|
||||
int i;
|
||||
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
|
||||
inline_dentry = inline_data_addr(page);
|
||||
bit_pos = dentry - inline_dentry->dentry;
|
||||
for (i = 0; i < slots; i++)
|
||||
test_and_clear_bit_le(bit_pos + i,
|
||||
&inline_dentry->dentry_bitmap);
|
||||
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
||||
f2fs_mark_inode_dirty_sync(dir);
|
||||
|
||||
if (inode)
|
||||
f2fs_drop_nlink(dir, inode);
|
||||
}
|
||||
|
||||
bool f2fs_empty_inline_dir(struct inode *dir)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct page *ipage;
|
||||
unsigned int bit_pos = 2;
|
||||
struct f2fs_inline_dentry *dentry_blk;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return false;
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
|
||||
NR_INLINE_DENTRY,
|
||||
bit_pos);
|
||||
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
||||
if (bit_pos < NR_INLINE_DENTRY)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
|
||||
struct fscrypt_str *fstr)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct f2fs_inline_dentry *inline_dentry = NULL;
|
||||
struct page *ipage = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
|
||||
if (ctx->pos == NR_INLINE_DENTRY)
|
||||
return 0;
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(inode, &d, (void *)inline_dentry, 2);
|
||||
|
||||
if (!f2fs_fill_dentries(ctx, &d, 0, fstr))
|
||||
ctx->pos = NR_INLINE_DENTRY;
|
||||
|
||||
f2fs_put_page(ipage, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_inline_data_fiemap(struct inode *inode,
|
||||
struct fiemap_extent_info *fieinfo, __u64 start, __u64 len)
|
||||
{
|
||||
__u64 byteaddr, ilen;
|
||||
__u32 flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED |
|
||||
FIEMAP_EXTENT_LAST;
|
||||
struct node_info ni;
|
||||
struct page *ipage;
|
||||
int err = 0;
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
if (!f2fs_has_inline_data(inode)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ilen = min_t(size_t, MAX_INLINE_DATA, i_size_read(inode));
|
||||
if (start >= ilen)
|
||||
goto out;
|
||||
if (start + len < ilen)
|
||||
ilen = start + len;
|
||||
ilen -= start;
|
||||
|
||||
get_node_info(F2FS_I_SB(inode), inode->i_ino, &ni);
|
||||
byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits;
|
||||
byteaddr += (char *)inline_data_addr(ipage) - (char *)F2FS_INODE(ipage);
|
||||
err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags);
|
||||
out:
|
||||
f2fs_put_page(ipage, 1);
|
||||
return err;
|
||||
}
|
||||
|
|
203
fs/f2fs/inode.c
203
fs/f2fs/inode.c
|
@ -12,13 +12,19 @@
|
|||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
void f2fs_mark_inode_dirty_sync(struct inode *inode)
|
||||
{
|
||||
if (f2fs_inode_dirtied(inode))
|
||||
return;
|
||||
mark_inode_dirty_sync(inode);
|
||||
}
|
||||
|
||||
void f2fs_set_inode_flags(struct inode *inode)
|
||||
{
|
||||
unsigned int flags = F2FS_I(inode)->i_flags;
|
||||
|
@ -34,8 +40,9 @@ void f2fs_set_inode_flags(struct inode *inode)
|
|||
new_fl |= S_NOATIME;
|
||||
if (flags & FS_DIRSYNC_FL)
|
||||
new_fl |= S_DIRSYNC;
|
||||
set_mask_bits(&inode->i_flags,
|
||||
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
|
||||
inode_set_flags(inode, new_fl,
|
||||
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
}
|
||||
|
||||
static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
|
@ -51,6 +58,15 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
|||
}
|
||||
}
|
||||
|
||||
static bool __written_first_block(struct f2fs_inode *ri)
|
||||
{
|
||||
block_t addr = le32_to_cpu(ri->i_addr[0]);
|
||||
|
||||
if (addr != NEW_ADDR && addr != NULL_ADDR)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
{
|
||||
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
||||
|
@ -67,6 +83,25 @@ static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
|||
}
|
||||
}
|
||||
|
||||
static void __recover_inline_status(struct inode *inode, struct page *ipage)
|
||||
{
|
||||
void *inline_data = inline_data_addr(ipage);
|
||||
__le32 *start = inline_data;
|
||||
__le32 *end = start + MAX_INLINE_DATA / sizeof(__le32);
|
||||
|
||||
while (start < end) {
|
||||
if (*start++) {
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
set_raw_inline(inode, F2FS_INODE(ipage));
|
||||
set_page_dirty(ipage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int do_read_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
|
@ -111,13 +146,30 @@ static int do_read_inode(struct inode *inode)
|
|||
fi->i_pino = le32_to_cpu(ri->i_pino);
|
||||
fi->i_dir_level = ri->i_dir_level;
|
||||
|
||||
get_extent_info(&fi->ext, ri->i_ext);
|
||||
get_inline_info(fi, ri);
|
||||
if (f2fs_init_extent_tree(inode, &ri->i_ext))
|
||||
set_page_dirty(node_page);
|
||||
|
||||
get_inline_info(inode, ri);
|
||||
|
||||
/* check data exist */
|
||||
if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
|
||||
__recover_inline_status(inode, node_page);
|
||||
|
||||
/* get rdev by using inline_info */
|
||||
__get_inode_rdev(inode, ri);
|
||||
|
||||
if (__written_first_block(ri))
|
||||
set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
|
||||
|
||||
if (!need_inode_block_update(sbi, inode->i_ino))
|
||||
fi->last_disk_size = inode->i_size;
|
||||
|
||||
f2fs_put_page(node_page, 1);
|
||||
|
||||
stat_inc_inline_xattr(inode);
|
||||
stat_inc_inline_inode(inode);
|
||||
stat_inc_inline_dir(inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -156,9 +208,13 @@ make_now:
|
|||
inode->i_op = &f2fs_dir_inode_operations;
|
||||
inode->i_fop = &f2fs_dir_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
|
||||
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
||||
else
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
|
@ -178,11 +234,13 @@ bad_inode:
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void update_inode(struct inode *inode, struct page *node_page)
|
||||
int update_inode(struct inode *inode, struct page *node_page)
|
||||
{
|
||||
struct f2fs_inode *ri;
|
||||
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
f2fs_inode_synced(inode);
|
||||
|
||||
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||
|
||||
ri = F2FS_INODE(node_page);
|
||||
|
||||
|
@ -193,8 +251,13 @@ void update_inode(struct inode *inode, struct page *node_page)
|
|||
ri->i_links = cpu_to_le32(inode->i_nlink);
|
||||
ri->i_size = cpu_to_le64(i_size_read(inode));
|
||||
ri->i_blocks = cpu_to_le64(inode->i_blocks);
|
||||
set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext);
|
||||
set_raw_inline(F2FS_I(inode), ri);
|
||||
|
||||
if (F2FS_I(inode)->extent_tree)
|
||||
set_raw_extent(&F2FS_I(inode)->extent_tree->largest,
|
||||
&ri->i_ext);
|
||||
else
|
||||
memset(&ri->i_ext, 0, sizeof(ri->i_ext));
|
||||
set_raw_inline(inode, ri);
|
||||
|
||||
ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
|
||||
ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
|
||||
|
@ -211,15 +274,19 @@ void update_inode(struct inode *inode, struct page *node_page)
|
|||
|
||||
__set_inode_rdev(inode, ri);
|
||||
set_cold_node(inode, node_page);
|
||||
set_page_dirty(node_page);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void update_inode_page(struct inode *inode)
|
||||
int update_inode_page(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct page *node_page;
|
||||
int ret = 0;
|
||||
retry:
|
||||
node_page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(node_page)) {
|
||||
|
@ -228,12 +295,14 @@ retry:
|
|||
cond_resched();
|
||||
goto retry;
|
||||
} else if (err != -ENOENT) {
|
||||
f2fs_stop_checkpoint(sbi);
|
||||
f2fs_stop_checkpoint(sbi, false);
|
||||
}
|
||||
return;
|
||||
f2fs_inode_synced(inode);
|
||||
return 0;
|
||||
}
|
||||
update_inode(inode, node_page);
|
||||
ret = update_inode(inode, node_page);
|
||||
f2fs_put_page(node_page, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
|
@ -244,20 +313,15 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||
inode->i_ino == F2FS_META_INO(sbi))
|
||||
return 0;
|
||||
|
||||
if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE))
|
||||
if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We need to lock here to prevent from producing dirty node pages
|
||||
* We need to balance fs here to prevent from producing dirty node pages
|
||||
* during the urgent cleaning time when runing out of free sections.
|
||||
*/
|
||||
f2fs_lock_op(sbi);
|
||||
update_inode_page(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
if (wbc)
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
if (update_inode_page(inode))
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -268,46 +332,74 @@ void f2fs_evict_inode(struct inode *inode)
|
|||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
int err = 0;
|
||||
|
||||
/* some remained atomic pages should discarded */
|
||||
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode))
|
||||
commit_inmem_pages(inode, true);
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
drop_inmem_pages(inode);
|
||||
|
||||
trace_f2fs_evict_inode(inode);
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
truncate_inode_pages(&inode->i_data, 0);
|
||||
|
||||
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
|
||||
inode->i_ino == F2FS_META_INO(sbi))
|
||||
goto out_clear;
|
||||
|
||||
f2fs_bug_on(sbi, get_dirty_pages(inode));
|
||||
remove_dirty_dir_inode(inode);
|
||||
remove_dirty_inode(inode);
|
||||
|
||||
f2fs_destroy_extent_tree(inode);
|
||||
|
||||
if (inode->i_nlink || is_bad_inode(inode))
|
||||
goto no_delete;
|
||||
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(FAULT_EVICT_INODE))
|
||||
goto no_delete;
|
||||
#endif
|
||||
|
||||
sb_start_intwrite(inode->i_sb);
|
||||
set_inode_flag(F2FS_I(inode), FI_NO_ALLOC);
|
||||
set_inode_flag(inode, FI_NO_ALLOC);
|
||||
i_size_write(inode, 0);
|
||||
|
||||
retry:
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
f2fs_truncate(inode);
|
||||
err = f2fs_truncate(inode);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
remove_inode_page(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
if (!err) {
|
||||
f2fs_lock_op(sbi);
|
||||
err = remove_inode_page(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
}
|
||||
|
||||
/* give more chances, if ENOMEM case */
|
||||
if (err == -ENOMEM) {
|
||||
err = 0;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (err)
|
||||
update_inode_page(inode);
|
||||
sb_end_intwrite(inode->i_sb);
|
||||
no_delete:
|
||||
stat_dec_inline_xattr(inode);
|
||||
stat_dec_inline_dir(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino);
|
||||
if (xnid)
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_APPEND_WRITE))
|
||||
add_dirty_inode(sbi, inode->i_ino, APPEND_INO);
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE))
|
||||
add_dirty_inode(sbi, inode->i_ino, UPDATE_INO);
|
||||
if (is_inode_flag_set(inode, FI_APPEND_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, APPEND_INO);
|
||||
if (is_inode_flag_set(inode, FI_UPDATE_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
|
||||
if (is_inode_flag_set(inode, FI_FREE_NID)) {
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
clear_inode_flag(inode, FI_FREE_NID);
|
||||
}
|
||||
f2fs_bug_on(sbi, err &&
|
||||
!exist_written_data(sbi, inode->i_ino, ORPHAN_INO));
|
||||
out_clear:
|
||||
fscrypt_put_encryption_info(inode, NULL);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
@ -315,19 +407,32 @@ out_clear:
|
|||
void handle_failed_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct node_info ni;
|
||||
|
||||
clear_nlink(inode);
|
||||
make_bad_inode(inode);
|
||||
/* don't make bad inode, since it becomes a regular file. */
|
||||
unlock_new_inode(inode);
|
||||
|
||||
i_size_write(inode, 0);
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
f2fs_truncate(inode);
|
||||
/*
|
||||
* Note: we should add inode to orphan list before f2fs_unlock_op()
|
||||
* so we can prevent losing this orphan when encoutering checkpoint
|
||||
* and following suddenly power-off.
|
||||
*/
|
||||
get_node_info(sbi, inode->i_ino, &ni);
|
||||
|
||||
remove_inode_page(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
if (ni.blk_addr != NULL_ADDR) {
|
||||
int err = acquire_orphan_inode(sbi);
|
||||
if (err) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"Too many orphan inodes, run fsck to fix.");
|
||||
} else {
|
||||
add_orphan_inode(inode);
|
||||
}
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
} else {
|
||||
set_inode_flag(inode, FI_FREE_NID);
|
||||
}
|
||||
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
/* iput will drop the inode object */
|
||||
|
|
724
fs/f2fs/namei.c
724
fs/f2fs/namei.c
File diff suppressed because it is too large
Load diff
1131
fs/f2fs/node.c
1131
fs/f2fs/node.c
File diff suppressed because it is too large
Load diff
124
fs/f2fs/node.h
124
fs/f2fs/node.h
|
@ -14,21 +14,38 @@
|
|||
/* node block offset on the NAT area dedicated to the given start node id */
|
||||
#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
|
||||
|
||||
/* # of pages to perform readahead before building free nids */
|
||||
#define FREE_NID_PAGES 4
|
||||
/* # of pages to perform synchronous readahead before building free nids */
|
||||
#define FREE_NID_PAGES 8
|
||||
#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES)
|
||||
|
||||
#define DEF_RA_NID_PAGES 0 /* # of nid pages to be readaheaded */
|
||||
|
||||
/* maximum readahead size for node during getting data blocks */
|
||||
#define MAX_RA_NODE 128
|
||||
|
||||
/* control the memory footprint threshold (10MB per 1GB ram) */
|
||||
#define DEF_RAM_THRESHOLD 10
|
||||
#define DEF_RAM_THRESHOLD 1
|
||||
|
||||
/* control dirty nats ratio threshold (default: 10% over max nid count) */
|
||||
#define DEF_DIRTY_NAT_RATIO_THRESHOLD 10
|
||||
/* control total # of nats */
|
||||
#define DEF_NAT_CACHE_THRESHOLD 100000
|
||||
|
||||
/* vector size for gang look-up from nat cache that consists of radix tree */
|
||||
#define NATVEC_SIZE 64
|
||||
#define SETVEC_SIZE 32
|
||||
|
||||
/* return value for read_node_page */
|
||||
#define LOCKED_PAGE 1
|
||||
|
||||
/* For flag in struct node_info */
|
||||
enum {
|
||||
IS_CHECKPOINTED, /* is it checkpointed before? */
|
||||
HAS_FSYNCED_INODE, /* is the inode fsynced before? */
|
||||
HAS_LAST_FSYNC, /* has the latest node fsync mark? */
|
||||
IS_DIRTY, /* this nat entry is dirty? */
|
||||
};
|
||||
|
||||
/*
|
||||
* For node information
|
||||
*/
|
||||
|
@ -37,18 +54,11 @@ struct node_info {
|
|||
nid_t ino; /* inode number of the node's owner */
|
||||
block_t blk_addr; /* block address of the node */
|
||||
unsigned char version; /* version of the node */
|
||||
};
|
||||
|
||||
enum {
|
||||
IS_CHECKPOINTED, /* is it checkpointed before? */
|
||||
HAS_FSYNCED_INODE, /* is the inode fsynced before? */
|
||||
HAS_LAST_FSYNC, /* has the latest node fsync mark? */
|
||||
IS_DIRTY, /* this nat entry is dirty? */
|
||||
unsigned char flag; /* for node information bits */
|
||||
};
|
||||
|
||||
struct nat_entry {
|
||||
struct list_head list; /* for clean or dirty nat list */
|
||||
unsigned char flag; /* for node information bits */
|
||||
struct node_info ni; /* in-memory node information */
|
||||
};
|
||||
|
||||
|
@ -63,20 +73,30 @@ struct nat_entry {
|
|||
|
||||
#define inc_node_version(version) (++version)
|
||||
|
||||
static inline void copy_node_info(struct node_info *dst,
|
||||
struct node_info *src)
|
||||
{
|
||||
dst->nid = src->nid;
|
||||
dst->ino = src->ino;
|
||||
dst->blk_addr = src->blk_addr;
|
||||
dst->version = src->version;
|
||||
/* should not copy flag here */
|
||||
}
|
||||
|
||||
static inline void set_nat_flag(struct nat_entry *ne,
|
||||
unsigned int type, bool set)
|
||||
{
|
||||
unsigned char mask = 0x01 << type;
|
||||
if (set)
|
||||
ne->flag |= mask;
|
||||
ne->ni.flag |= mask;
|
||||
else
|
||||
ne->flag &= ~mask;
|
||||
ne->ni.flag &= ~mask;
|
||||
}
|
||||
|
||||
static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type)
|
||||
{
|
||||
unsigned char mask = 0x01 << type;
|
||||
return ne->flag & mask;
|
||||
return ne->ni.flag & mask;
|
||||
}
|
||||
|
||||
static inline void nat_reset_flag(struct nat_entry *ne)
|
||||
|
@ -103,10 +123,24 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
|
|||
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;
|
||||
}
|
||||
|
||||
static inline bool excess_cached_nats(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->nat_cnt >= DEF_NAT_CACHE_THRESHOLD;
|
||||
}
|
||||
|
||||
enum mem_type {
|
||||
FREE_NIDS, /* indicates the free nid list */
|
||||
NAT_ENTRIES, /* indicates the cached nat entry */
|
||||
DIRTY_DENTS /* indicates dirty dentry pages */
|
||||
DIRTY_DENTS, /* indicates dirty dentry pages */
|
||||
INO_ENTRIES, /* indicates inode entries */
|
||||
EXTENT_CACHE, /* indicates extent cache */
|
||||
BASE_CHECK, /* check kernel status */
|
||||
};
|
||||
|
||||
struct nat_entry_set {
|
||||
|
@ -166,7 +200,7 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
|
|||
|
||||
block_addr = (pgoff_t)(nm_i->nat_blkaddr +
|
||||
(seg_off << sbi->log_blocks_per_seg << 1) +
|
||||
(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
|
||||
(block_off & (sbi->blocks_per_seg - 1)));
|
||||
|
||||
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
|
||||
block_addr += sbi->blocks_per_seg;
|
||||
|
@ -192,21 +226,26 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid)
|
|||
{
|
||||
unsigned int block_off = NAT_BLOCK_OFFSET(start_nid);
|
||||
|
||||
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
|
||||
f2fs_clear_bit(block_off, nm_i->nat_bitmap);
|
||||
else
|
||||
f2fs_set_bit(block_off, nm_i->nat_bitmap);
|
||||
f2fs_change_bit(block_off, nm_i->nat_bitmap);
|
||||
}
|
||||
|
||||
static inline void fill_node_footer(struct page *page, nid_t nid,
|
||||
nid_t ino, unsigned int ofs, bool reset)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(page);
|
||||
unsigned int old_flag = 0;
|
||||
|
||||
if (reset)
|
||||
memset(rn, 0, sizeof(*rn));
|
||||
else
|
||||
old_flag = le32_to_cpu(rn->footer.flag);
|
||||
|
||||
rn->footer.nid = cpu_to_le32(nid);
|
||||
rn->footer.ino = cpu_to_le32(ino);
|
||||
rn->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
|
||||
|
||||
/* should remain old flag bits such as COLD_BIT_SHIFT */
|
||||
rn->footer.flag = cpu_to_le32((ofs << OFFSET_BIT_SHIFT) |
|
||||
(old_flag & OFFSET_BIT_MASK));
|
||||
}
|
||||
|
||||
static inline void copy_node_footer(struct page *dst, struct page *src)
|
||||
|
@ -295,17 +334,17 @@ static inline bool IS_DNODE(struct page *node_page)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
|
||||
static inline int set_nid(struct page *p, int off, nid_t nid, bool i)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(p);
|
||||
|
||||
f2fs_wait_on_page_writeback(p, NODE);
|
||||
f2fs_wait_on_page_writeback(p, NODE, true);
|
||||
|
||||
if (i)
|
||||
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
|
||||
else
|
||||
rn->in.nid[off] = cpu_to_le32(nid);
|
||||
set_page_dirty(p);
|
||||
return set_page_dirty(p);
|
||||
}
|
||||
|
||||
static inline nid_t get_nid(struct page *p, int off, bool i)
|
||||
|
@ -323,28 +362,6 @@ static inline nid_t get_nid(struct page *p, int off, bool i)
|
|||
* - Mark cold node blocks in their node footer
|
||||
* - Mark cold data pages in page cache
|
||||
*/
|
||||
static inline int is_file(struct inode *inode, int type)
|
||||
{
|
||||
return F2FS_I(inode)->i_advise & type;
|
||||
}
|
||||
|
||||
static inline void set_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise |= type;
|
||||
}
|
||||
|
||||
static inline void clear_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise &= ~type;
|
||||
}
|
||||
|
||||
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
|
||||
static inline int is_cold_data(struct page *page)
|
||||
{
|
||||
return PageChecked(page);
|
||||
|
@ -370,6 +387,21 @@ static inline int is_node(struct page *page, int type)
|
|||
#define is_fsync_dnode(page) is_node(page, FSYNC_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)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(page);
|
||||
|
|
|
@ -49,8 +49,9 @@ static struct kmem_cache *fsync_entry_slab;
|
|||
|
||||
bool space_for_roll_forward(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (sbi->last_valid_block_count + sbi->alloc_valid_block_count
|
||||
> sbi->user_block_count)
|
||||
s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count);
|
||||
|
||||
if (sbi->last_valid_block_count + nalloc > sbi->user_block_count)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -67,7 +68,30 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int recover_dentry(struct inode *inode, struct page *ipage)
|
||||
static struct fsync_inode_entry *add_fsync_inode(struct list_head *head,
|
||||
struct inode *inode)
|
||||
{
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
entry->inode = inode;
|
||||
list_add_tail(&entry->list, head);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void del_fsync_inode(struct fsync_inode_entry *entry)
|
||||
{
|
||||
iput(entry->inode);
|
||||
list_del(&entry->list);
|
||||
kmem_cache_free(fsync_entry_slab, entry);
|
||||
}
|
||||
|
||||
static int recover_dentry(struct inode *inode, struct page *ipage,
|
||||
struct list_head *dir_list)
|
||||
{
|
||||
struct f2fs_inode *raw_inode = F2FS_INODE(ipage);
|
||||
nid_t pino = le32_to_cpu(raw_inode->i_pino);
|
||||
|
@ -75,28 +99,43 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
|
|||
struct qstr name;
|
||||
struct page *page;
|
||||
struct inode *dir, *einode;
|
||||
struct fsync_inode_entry *entry;
|
||||
int err = 0;
|
||||
|
||||
dir = f2fs_iget(inode->i_sb, pino);
|
||||
if (IS_ERR(dir)) {
|
||||
err = PTR_ERR(dir);
|
||||
goto out;
|
||||
entry = get_fsync_inode(dir_list, pino);
|
||||
if (!entry) {
|
||||
dir = f2fs_iget(inode->i_sb, pino);
|
||||
if (IS_ERR(dir)) {
|
||||
err = PTR_ERR(dir);
|
||||
goto out;
|
||||
}
|
||||
|
||||
entry = add_fsync_inode(dir_list, dir);
|
||||
if (!entry) {
|
||||
err = -ENOMEM;
|
||||
iput(dir);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dir = entry->inode;
|
||||
|
||||
if (file_enc_name(inode))
|
||||
return 0;
|
||||
|
||||
name.len = le32_to_cpu(raw_inode->i_namelen);
|
||||
name.name = raw_inode->i_name;
|
||||
|
||||
if (unlikely(name.len > F2FS_NAME_LEN)) {
|
||||
WARN_ON(1);
|
||||
err = -ENAMETOOLONG;
|
||||
goto out_err;
|
||||
goto out;
|
||||
}
|
||||
retry:
|
||||
de = f2fs_find_entry(dir, &name, &page);
|
||||
if (de && inode->i_ino == le32_to_cpu(de->ino)) {
|
||||
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
|
||||
if (de && inode->i_ino == le32_to_cpu(de->ino))
|
||||
goto out_unmap_put;
|
||||
}
|
||||
|
||||
if (de) {
|
||||
einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino));
|
||||
if (IS_ERR(einode)) {
|
||||
|
@ -111,28 +150,20 @@ retry:
|
|||
iput(einode);
|
||||
goto out_unmap_put;
|
||||
}
|
||||
f2fs_delete_entry(de, page, einode);
|
||||
f2fs_delete_entry(de, page, dir, einode);
|
||||
iput(einode);
|
||||
goto retry;
|
||||
}
|
||||
err = __f2fs_add_link(dir, &name, inode);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(dir), FI_DELAY_IPUT)) {
|
||||
iput(dir);
|
||||
} else if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
} else {
|
||||
add_dirty_dir_inode(dir);
|
||||
set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT);
|
||||
err = __f2fs_add_link(dir, &name, inode,
|
||||
inode->i_ino, inode->i_mode);
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
out_unmap_put:
|
||||
kunmap(page);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
out_err:
|
||||
iput(dir);
|
||||
out:
|
||||
f2fs_msg(inode->i_sb, KERN_NOTICE,
|
||||
"%s: ino = %x, name = %s, dir = %lx, err = %d",
|
||||
|
@ -144,9 +175,10 @@ out:
|
|||
static void recover_inode(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_inode *raw = F2FS_INODE(page);
|
||||
char *name;
|
||||
|
||||
inode->i_mode = le16_to_cpu(raw->i_mode);
|
||||
i_size_write(inode, le64_to_cpu(raw->i_size));
|
||||
f2fs_i_size_write(inode, le64_to_cpu(raw->i_size));
|
||||
inode->i_atime.tv_sec = le64_to_cpu(raw->i_mtime);
|
||||
inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime);
|
||||
inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime);
|
||||
|
@ -154,14 +186,46 @@ static void recover_inode(struct inode *inode, struct page *page)
|
|||
inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec);
|
||||
inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
||||
|
||||
if (file_enc_name(inode))
|
||||
name = "<encrypted>";
|
||||
else
|
||||
name = F2FS_INODE(page)->i_name;
|
||||
|
||||
f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s",
|
||||
ino_of_node(page), F2FS_INODE(page)->i_name);
|
||||
ino_of_node(page), name);
|
||||
}
|
||||
|
||||
static bool is_same_inode(struct inode *inode, struct page *ipage)
|
||||
{
|
||||
struct f2fs_inode *ri = F2FS_INODE(ipage);
|
||||
struct timespec disk;
|
||||
|
||||
if (!IS_INODE(ipage))
|
||||
return true;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_ctime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec);
|
||||
if (timespec_compare(&inode->i_ctime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_atime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec);
|
||||
if (timespec_compare(&inode->i_atime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_mtime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec);
|
||||
if (timespec_compare(&inode->i_mtime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
{
|
||||
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||
struct curseg_info *curseg;
|
||||
struct inode *inode;
|
||||
struct page *page = NULL;
|
||||
block_t blkaddr;
|
||||
int err = 0;
|
||||
|
@ -173,10 +237,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||
while (1) {
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
|
||||
if (!is_valid_blkaddr(sbi, blkaddr, META_POR))
|
||||
return 0;
|
||||
|
||||
page = get_meta_page_ra(sbi, blkaddr);
|
||||
page = get_tmp_page(sbi, blkaddr);
|
||||
|
||||
if (cp_ver != cpver_of_node(page))
|
||||
break;
|
||||
|
@ -186,9 +250,8 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||
|
||||
entry = get_fsync_inode(head, ino_of_node(page));
|
||||
if (entry) {
|
||||
if (IS_INODE(page) && is_dent_dnode(page))
|
||||
set_inode_flag(F2FS_I(entry->inode),
|
||||
FI_INC_LINK);
|
||||
if (!is_same_inode(entry->inode, page))
|
||||
goto next;
|
||||
} else {
|
||||
if (IS_INODE(page) && is_dent_dnode(page)) {
|
||||
err = recover_inode_page(sbi, page);
|
||||
|
@ -196,37 +259,38 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||
break;
|
||||
}
|
||||
|
||||
/* add this fsync inode to the list */
|
||||
entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
|
||||
if (!entry) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* CP | dnode(F) | inode(DF)
|
||||
* For this case, we should not give up now.
|
||||
*/
|
||||
entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
|
||||
if (IS_ERR(entry->inode)) {
|
||||
err = PTR_ERR(entry->inode);
|
||||
kmem_cache_free(fsync_entry_slab, entry);
|
||||
if (err == -ENOENT)
|
||||
inode = f2fs_iget(sbi->sb, ino_of_node(page));
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
if (err == -ENOENT) {
|
||||
err = 0;
|
||||
goto next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* add this fsync inode to the list */
|
||||
entry = add_fsync_inode(head, inode);
|
||||
if (!entry) {
|
||||
err = -ENOMEM;
|
||||
iput(inode);
|
||||
break;
|
||||
}
|
||||
list_add_tail(&entry->list, head);
|
||||
}
|
||||
entry->blkaddr = blkaddr;
|
||||
|
||||
if (IS_INODE(page)) {
|
||||
entry->last_inode = blkaddr;
|
||||
if (is_dent_dnode(page))
|
||||
entry->last_dentry = blkaddr;
|
||||
}
|
||||
if (IS_INODE(page) && is_dent_dnode(page))
|
||||
entry->last_dentry = blkaddr;
|
||||
next:
|
||||
/* check next segment */
|
||||
blkaddr = next_blkaddr_of_node(page);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
ra_meta_pages_cond(sbi, blkaddr);
|
||||
}
|
||||
f2fs_put_page(page, 1);
|
||||
return err;
|
||||
|
@ -236,11 +300,8 @@ static void destroy_fsync_dnodes(struct list_head *head)
|
|||
{
|
||||
struct fsync_inode_entry *entry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, head, list) {
|
||||
iput(entry->inode);
|
||||
list_del(&entry->list);
|
||||
kmem_cache_free(fsync_entry_slab, entry);
|
||||
}
|
||||
list_for_each_entry_safe(entry, tmp, head, list)
|
||||
del_fsync_inode(entry);
|
||||
}
|
||||
|
||||
static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
||||
|
@ -252,6 +313,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||
struct f2fs_summary_block *sum_node;
|
||||
struct f2fs_summary sum;
|
||||
struct page *sum_page, *node_page;
|
||||
struct dnode_of_data tdn = *dn;
|
||||
nid_t ino, nid;
|
||||
struct inode *inode;
|
||||
unsigned int offset;
|
||||
|
@ -279,17 +341,15 @@ got_it:
|
|||
/* Use the locked dnode page and inode */
|
||||
nid = le32_to_cpu(sum.nid);
|
||||
if (dn->inode->i_ino == nid) {
|
||||
struct dnode_of_data tdn = *dn;
|
||||
tdn.nid = nid;
|
||||
if (!dn->inode_page_locked)
|
||||
lock_page(dn->inode_page);
|
||||
tdn.node_page = dn->inode_page;
|
||||
tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node);
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
return 0;
|
||||
goto truncate_out;
|
||||
} else if (dn->nid == nid) {
|
||||
struct dnode_of_data tdn = *dn;
|
||||
tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node);
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
return 0;
|
||||
goto truncate_out;
|
||||
}
|
||||
|
||||
/* Get the node page */
|
||||
|
@ -310,38 +370,54 @@ got_it:
|
|||
inode = dn->inode;
|
||||
}
|
||||
|
||||
bidx = start_bidx_of_node(offset, F2FS_I(inode)) +
|
||||
le16_to_cpu(sum.ofs_in_node);
|
||||
bidx = start_bidx_of_node(offset, inode) + le16_to_cpu(sum.ofs_in_node);
|
||||
|
||||
if (ino != dn->inode->i_ino) {
|
||||
truncate_hole(inode, bidx, bidx + 1);
|
||||
/*
|
||||
* if inode page is locked, unlock temporarily, but its reference
|
||||
* count keeps alive.
|
||||
*/
|
||||
if (ino == dn->inode->i_ino && dn->inode_page_locked)
|
||||
unlock_page(dn->inode_page);
|
||||
|
||||
set_new_dnode(&tdn, inode, NULL, NULL, 0);
|
||||
if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE))
|
||||
goto out;
|
||||
|
||||
if (tdn.data_blkaddr == blkaddr)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
|
||||
f2fs_put_dnode(&tdn);
|
||||
out:
|
||||
if (ino != dn->inode->i_ino)
|
||||
iput(inode);
|
||||
} else {
|
||||
struct dnode_of_data tdn;
|
||||
set_new_dnode(&tdn, inode, dn->inode_page, NULL, 0);
|
||||
if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE))
|
||||
return 0;
|
||||
if (tdn.data_blkaddr != NULL_ADDR)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
f2fs_put_page(tdn.node_page, 1);
|
||||
}
|
||||
else if (dn->inode_page_locked)
|
||||
lock_page(dn->inode_page);
|
||||
return 0;
|
||||
|
||||
truncate_out:
|
||||
if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
if (dn->inode->i_ino == nid && !dn->inode_page_locked)
|
||||
unlock_page(dn->inode_page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
struct page *page, block_t blkaddr)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
unsigned int start, end;
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
unsigned int start, end;
|
||||
int err = 0, recovered = 0;
|
||||
|
||||
/* step 1: recover xattr */
|
||||
if (IS_INODE(page)) {
|
||||
recover_inline_xattr(inode, page);
|
||||
} else if (f2fs_has_xattr_block(ofs_of_node(page))) {
|
||||
/*
|
||||
* Deprecated; xattr blocks should be found from cold log.
|
||||
* But, we should remain this for backward compatibility.
|
||||
*/
|
||||
recover_xattr_data(inode, page, blkaddr);
|
||||
goto out;
|
||||
}
|
||||
|
@ -351,36 +427,63 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||
goto out;
|
||||
|
||||
/* step 3: recover data indices */
|
||||
start = start_bidx_of_node(ofs_of_node(page), fi);
|
||||
end = start + ADDRS_PER_PAGE(page, fi);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
start = start_bidx_of_node(ofs_of_node(page), inode);
|
||||
end = start + ADDRS_PER_PAGE(page, inode);
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
|
||||
err = get_dnode_of_data(&dn, start, ALLOC_NODE);
|
||||
if (err) {
|
||||
f2fs_unlock_op(sbi);
|
||||
if (err)
|
||||
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);
|
||||
f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
|
||||
f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page));
|
||||
|
||||
for (; start < end; start++) {
|
||||
for (; start < end; start++, dn.ofs_in_node++) {
|
||||
block_t src, dest;
|
||||
|
||||
src = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||
dest = datablock_addr(page, dn.ofs_in_node);
|
||||
|
||||
if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR) {
|
||||
/* skip recovering if dest is the same as src */
|
||||
if (src == dest)
|
||||
continue;
|
||||
|
||||
/* dest is invalid, just invalidate src block */
|
||||
if (dest == NULL_ADDR) {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((start + 1) << PAGE_SHIFT > i_size_read(inode))
|
||||
f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT);
|
||||
|
||||
/*
|
||||
* dest is reserved block, invalidate src block
|
||||
* and then reserve one new block in dnode page.
|
||||
*/
|
||||
if (dest == NEW_ADDR) {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
reserve_new_block(&dn);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* dest is valid block, try to recover from src to dest */
|
||||
if (is_valid_blkaddr(sbi, dest, META_POR)) {
|
||||
|
||||
if (src == NULL_ADDR) {
|
||||
err = reserve_new_block(&dn);
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
while (err)
|
||||
err = reserve_new_block(&dn);
|
||||
#endif
|
||||
/* We should not get -ENOSPC */
|
||||
f2fs_bug_on(sbi, err);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check the previous node page having this index */
|
||||
|
@ -388,28 +491,19 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||
if (err)
|
||||
goto err;
|
||||
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* write dummy data page */
|
||||
recover_data_page(sbi, NULL, &sum, src, dest);
|
||||
update_extent_cache(dest, &dn);
|
||||
f2fs_replace_block(sbi, &dn, src, dest,
|
||||
ni.version, false, false);
|
||||
recovered++;
|
||||
}
|
||||
dn.ofs_in_node++;
|
||||
}
|
||||
|
||||
/* write node page in place */
|
||||
set_summary(&sum, dn.nid, 0, 0);
|
||||
if (IS_INODE(dn.node_page))
|
||||
sync_inode_page(&dn);
|
||||
|
||||
copy_node_footer(dn.node_page, page);
|
||||
fill_node_footer(dn.node_page, dn.nid, ni.ino,
|
||||
ofs_of_node(page), false);
|
||||
set_page_dirty(dn.node_page);
|
||||
err:
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_unlock_op(sbi);
|
||||
out:
|
||||
f2fs_msg(sbi->sb, KERN_NOTICE,
|
||||
"recover_data: ino = %lx, recovered = %d blocks, err = %d",
|
||||
|
@ -417,8 +511,8 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int recover_data(struct f2fs_sb_info *sbi,
|
||||
struct list_head *head, int type)
|
||||
static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list,
|
||||
struct list_head *dir_list)
|
||||
{
|
||||
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||
struct curseg_info *curseg;
|
||||
|
@ -427,23 +521,25 @@ static int recover_data(struct f2fs_sb_info *sbi,
|
|||
block_t blkaddr;
|
||||
|
||||
/* get node pages in the current segment */
|
||||
curseg = CURSEG_I(sbi, type);
|
||||
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
|
||||
while (1) {
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
|
||||
if (!is_valid_blkaddr(sbi, blkaddr, META_POR))
|
||||
break;
|
||||
|
||||
page = get_meta_page_ra(sbi, blkaddr);
|
||||
ra_meta_pages_cond(sbi, blkaddr);
|
||||
|
||||
page = get_tmp_page(sbi, blkaddr);
|
||||
|
||||
if (cp_ver != cpver_of_node(page)) {
|
||||
f2fs_put_page(page, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
entry = get_fsync_inode(head, ino_of_node(page));
|
||||
entry = get_fsync_inode(inode_list, ino_of_node(page));
|
||||
if (!entry)
|
||||
goto next;
|
||||
/*
|
||||
|
@ -451,10 +547,10 @@ static int recover_data(struct f2fs_sb_info *sbi,
|
|||
* In this case, we can lose the latest inode(x).
|
||||
* So, call recover_inode for the inode update.
|
||||
*/
|
||||
if (entry->last_inode == blkaddr)
|
||||
if (IS_INODE(page))
|
||||
recover_inode(entry->inode, page);
|
||||
if (entry->last_dentry == blkaddr) {
|
||||
err = recover_dentry(entry->inode, page);
|
||||
err = recover_dentry(entry->inode, page, dir_list);
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
break;
|
||||
|
@ -466,11 +562,8 @@ static int recover_data(struct f2fs_sb_info *sbi,
|
|||
break;
|
||||
}
|
||||
|
||||
if (entry->blkaddr == blkaddr) {
|
||||
iput(entry->inode);
|
||||
list_del(&entry->list);
|
||||
kmem_cache_free(fsync_entry_slab, entry);
|
||||
}
|
||||
if (entry->blkaddr == blkaddr)
|
||||
del_fsync_inode(entry);
|
||||
next:
|
||||
/* check next segment */
|
||||
blkaddr = next_blkaddr_of_node(page);
|
||||
|
@ -481,12 +574,14 @@ next:
|
|||
return err;
|
||||
}
|
||||
|
||||
int recover_fsync_data(struct f2fs_sb_info *sbi)
|
||||
int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
||||
struct list_head inode_list;
|
||||
struct list_head dir_list;
|
||||
block_t blkaddr;
|
||||
int err;
|
||||
int ret = 0;
|
||||
bool need_writecp = false;
|
||||
|
||||
fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
|
||||
|
@ -495,58 +590,74 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
|
|||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&inode_list);
|
||||
|
||||
/* step #1: find fsynced inode numbers */
|
||||
sbi->por_doing = true;
|
||||
INIT_LIST_HEAD(&dir_list);
|
||||
|
||||
/* prevent checkpoint */
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
|
||||
/* step #1: find fsynced inode numbers */
|
||||
err = find_fsync_dnodes(sbi, &inode_list);
|
||||
if (err)
|
||||
if (err || list_empty(&inode_list))
|
||||
goto out;
|
||||
|
||||
if (list_empty(&inode_list))
|
||||
if (check_only) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
need_writecp = true;
|
||||
|
||||
/* step #2: recover data */
|
||||
err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
|
||||
err = recover_data(sbi, &inode_list, &dir_list);
|
||||
if (!err)
|
||||
f2fs_bug_on(sbi, !list_empty(&inode_list));
|
||||
out:
|
||||
destroy_fsync_dnodes(&inode_list);
|
||||
kmem_cache_destroy(fsync_entry_slab);
|
||||
|
||||
/* truncate meta pages to be used by the recovery */
|
||||
truncate_inode_pages_range(META_MAPPING(sbi),
|
||||
MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1);
|
||||
(loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1);
|
||||
|
||||
if (err) {
|
||||
truncate_inode_pages_final(NODE_MAPPING(sbi));
|
||||
truncate_inode_pages_final(META_MAPPING(sbi));
|
||||
truncate_inode_pages(NODE_MAPPING(sbi), 0);
|
||||
truncate_inode_pages(META_MAPPING(sbi), 0);
|
||||
}
|
||||
|
||||
sbi->por_doing = false;
|
||||
clear_sbi_flag(sbi, SBI_POR_DOING);
|
||||
if (err) {
|
||||
discard_next_dnode(sbi, blkaddr);
|
||||
bool invalidate = false;
|
||||
|
||||
if (test_opt(sbi, LFS)) {
|
||||
update_meta_page(sbi, NULL, blkaddr);
|
||||
invalidate = true;
|
||||
} else if (discard_next_dnode(sbi, blkaddr)) {
|
||||
invalidate = true;
|
||||
}
|
||||
|
||||
/* Flush all the NAT/SIT pages */
|
||||
while (get_pages(sbi, F2FS_DIRTY_META))
|
||||
sync_meta_pages(sbi, META, LONG_MAX);
|
||||
|
||||
/* invalidate temporary meta page */
|
||||
if (invalidate)
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
blkaddr, blkaddr);
|
||||
|
||||
set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
} else if (need_writecp) {
|
||||
struct cp_control cpc = {
|
||||
.reason = CP_SYNC,
|
||||
.reason = CP_RECOVERY,
|
||||
};
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
write_checkpoint(sbi, &cpc);
|
||||
err = write_checkpoint(sbi, &cpc);
|
||||
} else {
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
}
|
||||
return err;
|
||||
|
||||
destroy_fsync_dnodes(&dir_list);
|
||||
kmem_cache_destroy(fsync_entry_slab);
|
||||
return ret ? ret: err;
|
||||
}
|
||||
|
|
1037
fs/f2fs/segment.c
1037
fs/f2fs/segment.c
File diff suppressed because it is too large
Load diff
|
@ -15,6 +15,7 @@
|
|||
#define NULL_SECNO ((unsigned int)(~0))
|
||||
|
||||
#define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */
|
||||
#define DEF_MAX_RECLAIM_PREFREE_SEGMENTS 4096 /* 8GB in maximum */
|
||||
|
||||
/* L: Logical segment # in volume, R: Relative segment # in main area */
|
||||
#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno)
|
||||
|
@ -136,10 +137,12 @@ enum {
|
|||
/*
|
||||
* BG_GC means the background cleaning job.
|
||||
* FG_GC means the on-demand cleaning job.
|
||||
* FORCE_FG_GC means on-demand cleaning job in background.
|
||||
*/
|
||||
enum {
|
||||
BG_GC = 0,
|
||||
FG_GC
|
||||
FG_GC,
|
||||
FORCE_FG_GC,
|
||||
};
|
||||
|
||||
/* for a function parameter to select a victim segment */
|
||||
|
@ -155,15 +158,17 @@ struct victim_sel_policy {
|
|||
};
|
||||
|
||||
struct seg_entry {
|
||||
unsigned short valid_blocks; /* # of valid blocks */
|
||||
unsigned int type:6; /* segment type like CURSEG_XXX_TYPE */
|
||||
unsigned int valid_blocks:10; /* # of valid blocks */
|
||||
unsigned int ckpt_valid_blocks:10; /* # of valid blocks last cp */
|
||||
unsigned int padding:6; /* padding */
|
||||
unsigned char *cur_valid_map; /* validity bitmap of blocks */
|
||||
/*
|
||||
* # of valid blocks and the validity bitmap stored in the the last
|
||||
* checkpoint pack. This information is used by the SSR mode.
|
||||
*/
|
||||
unsigned short ckpt_valid_blocks;
|
||||
unsigned char *ckpt_valid_map;
|
||||
unsigned char type; /* segment type like CURSEG_XXX_TYPE */
|
||||
unsigned char *ckpt_valid_map; /* validity bitmap of blocks last cp */
|
||||
unsigned char *discard_map;
|
||||
unsigned long long mtime; /* modification time of the segment */
|
||||
};
|
||||
|
||||
|
@ -175,9 +180,19 @@ struct segment_allocation {
|
|||
void (*allocate_segment)(struct f2fs_sb_info *, int, bool);
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
|
||||
|
||||
#define IS_ATOMIC_WRITTEN_PAGE(page) \
|
||||
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
||||
|
||||
struct inmem_pages {
|
||||
struct list_head list;
|
||||
struct page *page;
|
||||
block_t old_addr; /* for revoking when fail to commit */
|
||||
};
|
||||
|
||||
struct sit_info {
|
||||
|
@ -189,6 +204,7 @@ struct sit_info {
|
|||
char *sit_bitmap; /* SIT bitmap pointer */
|
||||
unsigned int bitmap_size; /* SIT bitmap size */
|
||||
|
||||
unsigned long *tmp_map; /* bitmap for temporal use */
|
||||
unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */
|
||||
unsigned int dirty_sentries; /* # of dirty sentries */
|
||||
unsigned int sents_per_block; /* # of SIT entries per block */
|
||||
|
@ -207,7 +223,7 @@ struct free_segmap_info {
|
|||
unsigned int start_segno; /* start segment number logically */
|
||||
unsigned int free_segments; /* # of free segments */
|
||||
unsigned int free_sections; /* # of free sections */
|
||||
rwlock_t segmap_lock; /* free segmap lock */
|
||||
spinlock_t segmap_lock; /* free segmap lock */
|
||||
unsigned long *free_segmap; /* free segment bitmap */
|
||||
unsigned long *free_secmap; /* free section bitmap */
|
||||
};
|
||||
|
@ -243,6 +259,8 @@ struct victim_selection {
|
|||
struct curseg_info {
|
||||
struct mutex curseg_mutex; /* lock for consistency */
|
||||
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 int segno; /* current segment number */
|
||||
unsigned short next_blkoff; /* next block offset to write */
|
||||
|
@ -318,9 +336,9 @@ static inline unsigned int find_next_inuse(struct free_segmap_info *free_i,
|
|||
unsigned int max, unsigned int segno)
|
||||
{
|
||||
unsigned int ret;
|
||||
read_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
ret = find_next_bit(free_i->free_segmap, max, segno);
|
||||
read_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -331,16 +349,17 @@ static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno)
|
|||
unsigned int start_segno = secno * sbi->segs_per_sec;
|
||||
unsigned int next;
|
||||
|
||||
write_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
clear_bit(segno, free_i->free_segmap);
|
||||
free_i->free_segments++;
|
||||
|
||||
next = find_next_bit(free_i->free_segmap, MAIN_SEGS(sbi), start_segno);
|
||||
next = find_next_bit(free_i->free_segmap,
|
||||
start_segno + sbi->segs_per_sec, start_segno);
|
||||
if (next >= start_segno + sbi->segs_per_sec) {
|
||||
clear_bit(secno, free_i->free_secmap);
|
||||
free_i->free_sections++;
|
||||
}
|
||||
write_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
}
|
||||
|
||||
static inline void __set_inuse(struct f2fs_sb_info *sbi,
|
||||
|
@ -362,7 +381,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
|
|||
unsigned int start_segno = secno * sbi->segs_per_sec;
|
||||
unsigned int next;
|
||||
|
||||
write_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
if (test_and_clear_bit(segno, free_i->free_segmap)) {
|
||||
free_i->free_segments++;
|
||||
|
||||
|
@ -373,7 +392,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
|
|||
free_i->free_sections++;
|
||||
}
|
||||
}
|
||||
write_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
}
|
||||
|
||||
static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi,
|
||||
|
@ -381,13 +400,13 @@ static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int secno = segno / sbi->segs_per_sec;
|
||||
write_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
if (!test_and_set_bit(segno, free_i->free_segmap)) {
|
||||
free_i->free_segments--;
|
||||
if (!test_and_set_bit(secno, free_i->free_secmap))
|
||||
free_i->free_sections--;
|
||||
}
|
||||
write_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
}
|
||||
|
||||
static inline void get_sit_bitmap(struct f2fs_sb_info *sbi,
|
||||
|
@ -451,6 +470,10 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi)
|
|||
{
|
||||
int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
|
||||
int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
|
||||
|
||||
if (test_opt(sbi, LFS))
|
||||
return false;
|
||||
|
||||
return free_sections(sbi) <= (node_secs + 2 * dent_secs +
|
||||
reserved_sections(sbi) + 1);
|
||||
}
|
||||
|
@ -460,7 +483,9 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
|
|||
int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
|
||||
int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
|
||||
|
||||
if (unlikely(sbi->por_doing))
|
||||
node_secs += get_blocktype_secs(sbi, F2FS_DIRTY_IMETA);
|
||||
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
return false;
|
||||
|
||||
return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs +
|
||||
|
@ -512,6 +537,9 @@ static inline bool need_inplace_update(struct inode *inode)
|
|||
if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
|
||||
return false;
|
||||
|
||||
if (test_opt(sbi, LFS))
|
||||
return false;
|
||||
|
||||
if (policy & (0x1 << F2FS_IPU_FORCE))
|
||||
return true;
|
||||
if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi))
|
||||
|
@ -525,7 +553,7 @@ static inline bool need_inplace_update(struct inode *inode)
|
|||
|
||||
/* this is only set during fdatasync */
|
||||
if (policy & (0x1 << F2FS_IPU_FSYNC) &&
|
||||
is_inode_flag_set(F2FS_I(inode), FI_NEED_IPU))
|
||||
is_inode_flag_set(inode, FI_NEED_IPU))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -551,16 +579,15 @@ static inline unsigned short curseg_blkoff(struct f2fs_sb_info *sbi, int type)
|
|||
return curseg->next_blkoff;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
BUG_ON(segno > TOTAL_SEGS(sbi) - 1);
|
||||
f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1);
|
||||
}
|
||||
|
||||
static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
||||
{
|
||||
BUG_ON(blk_addr < SEG0_BLKADDR(sbi));
|
||||
BUG_ON(blk_addr >= MAX_BLKADDR(sbi));
|
||||
f2fs_bug_on(sbi, blk_addr < SEG0_BLKADDR(sbi)
|
||||
|| blk_addr >= MAX_BLKADDR(sbi));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -569,16 +596,11 @@ static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
|||
static inline void check_block_count(struct f2fs_sb_info *sbi,
|
||||
int segno, struct f2fs_sit_entry *raw_sit)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false;
|
||||
int valid_blocks = 0;
|
||||
int cur_pos = 0, next_pos;
|
||||
|
||||
/* check segment usage */
|
||||
BUG_ON(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg);
|
||||
|
||||
/* check boundary of a given segment number */
|
||||
BUG_ON(segno > TOTAL_SEGS(sbi) - 1);
|
||||
|
||||
/* check bitmap with valid block count */
|
||||
do {
|
||||
if (is_valid) {
|
||||
|
@ -594,35 +616,11 @@ static inline void check_block_count(struct f2fs_sb_info *sbi,
|
|||
is_valid = !is_valid;
|
||||
} while (cur_pos < sbi->blocks_per_seg);
|
||||
BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks);
|
||||
}
|
||||
#else
|
||||
static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
if (segno > TOTAL_SEGS(sbi) - 1)
|
||||
sbi->need_fsck = true;
|
||||
}
|
||||
|
||||
static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
||||
{
|
||||
if (blk_addr < SEG0_BLKADDR(sbi) || blk_addr >= MAX_BLKADDR(sbi))
|
||||
sbi->need_fsck = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Summary block is always treated as an invalid block
|
||||
*/
|
||||
static inline void check_block_count(struct f2fs_sb_info *sbi,
|
||||
int segno, struct f2fs_sit_entry *raw_sit)
|
||||
{
|
||||
/* check segment usage */
|
||||
if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg)
|
||||
sbi->need_fsck = true;
|
||||
|
||||
/* check boundary of a given segment number */
|
||||
if (segno > TOTAL_SEGS(sbi) - 1)
|
||||
sbi->need_fsck = true;
|
||||
}
|
||||
#endif
|
||||
/* check segment usage, and check boundary of a given segment number */
|
||||
f2fs_bug_on(sbi, GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg
|
||||
|| segno > TOTAL_SEGS(sbi) - 1);
|
||||
}
|
||||
|
||||
static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi,
|
||||
unsigned int start)
|
||||
|
@ -657,10 +655,7 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start)
|
|||
{
|
||||
unsigned int block_off = SIT_BLOCK_OFFSET(start);
|
||||
|
||||
if (f2fs_test_bit(block_off, sit_i->sit_bitmap))
|
||||
f2fs_clear_bit(block_off, sit_i->sit_bitmap);
|
||||
else
|
||||
f2fs_set_bit(block_off, sit_i->sit_bitmap);
|
||||
f2fs_change_bit(block_off, sit_i->sit_bitmap);
|
||||
}
|
||||
|
||||
static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi)
|
||||
|
@ -714,12 +709,15 @@ static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi)
|
|||
*/
|
||||
static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
if (sbi->sb->s_bdi->dirty_exceeded)
|
||||
return 0;
|
||||
|
||||
if (type == DATA)
|
||||
return sbi->blocks_per_seg;
|
||||
else if (type == NODE)
|
||||
return 3 * sbi->blocks_per_seg;
|
||||
return 8 * sbi->blocks_per_seg;
|
||||
else if (type == META)
|
||||
return MAX_BIO_BLOCKS(sbi);
|
||||
return 8 * MAX_BIO_BLOCKS(sbi);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
@ -737,10 +735,8 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type,
|
|||
|
||||
nr_to_write = wbc->nr_to_write;
|
||||
|
||||
if (type == DATA)
|
||||
desired = 4096;
|
||||
else if (type == NODE)
|
||||
desired = 3 * max_hw_blocks(sbi);
|
||||
if (type == NODE)
|
||||
desired = 2 * max_hw_blocks(sbi);
|
||||
else
|
||||
desired = MAX_BIO_BLOCKS(sbi);
|
||||
|
||||
|
|
141
fs/f2fs/shrinker.c
Normal file
141
fs/f2fs/shrinker.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* f2fs shrinker support
|
||||
* the basic infra was copied from fs/ubifs/shrinker.c
|
||||
*
|
||||
* Copyright (c) 2015 Motorola Mobility
|
||||
* Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
||||
static LIST_HEAD(f2fs_list);
|
||||
static DEFINE_SPINLOCK(f2fs_list_lock);
|
||||
static unsigned int shrinker_run_no;
|
||||
|
||||
static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt;
|
||||
}
|
||||
|
||||
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (NM_I(sbi)->fcnt > MAX_FREE_NIDS)
|
||||
return NM_I(sbi)->fcnt - MAX_FREE_NIDS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return atomic_read(&sbi->total_zombie_tree) +
|
||||
atomic_read(&sbi->total_ext_node);
|
||||
}
|
||||
|
||||
unsigned long f2fs_shrink_count(struct shrinker *shrink,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct list_head *p;
|
||||
unsigned long count = 0;
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
p = f2fs_list.next;
|
||||
while (p != &f2fs_list) {
|
||||
sbi = list_entry(p, struct f2fs_sb_info, s_list);
|
||||
|
||||
/* stop f2fs_put_super */
|
||||
if (!mutex_trylock(&sbi->umount_mutex)) {
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
|
||||
/* count extent cache entries */
|
||||
count += __count_extent_cache(sbi);
|
||||
|
||||
/* shrink clean nat cache entries */
|
||||
count += __count_nat_entries(sbi);
|
||||
|
||||
/* count free nids cache entries */
|
||||
count += __count_free_nids(sbi);
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
p = p->next;
|
||||
mutex_unlock(&sbi->umount_mutex);
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned long f2fs_shrink_scan(struct shrinker *shrink,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
unsigned long nr = sc->nr_to_scan;
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct list_head *p;
|
||||
unsigned int run_no;
|
||||
unsigned long freed = 0;
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
do {
|
||||
run_no = ++shrinker_run_no;
|
||||
} while (run_no == 0);
|
||||
p = f2fs_list.next;
|
||||
while (p != &f2fs_list) {
|
||||
sbi = list_entry(p, struct f2fs_sb_info, s_list);
|
||||
|
||||
if (sbi->shrinker_run_no == run_no)
|
||||
break;
|
||||
|
||||
/* stop f2fs_put_super */
|
||||
if (!mutex_trylock(&sbi->umount_mutex)) {
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
|
||||
sbi->shrinker_run_no = run_no;
|
||||
|
||||
/* shrink extent cache entries */
|
||||
freed += f2fs_shrink_extent_tree(sbi, nr >> 1);
|
||||
|
||||
/* shrink clean nat cache entries */
|
||||
if (freed < nr)
|
||||
freed += try_to_free_nats(sbi, nr - freed);
|
||||
|
||||
/* shrink free nids cache entries */
|
||||
if (freed < nr)
|
||||
freed += try_to_free_nids(sbi, nr - freed);
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
p = p->next;
|
||||
list_move_tail(&sbi->s_list, &f2fs_list);
|
||||
mutex_unlock(&sbi->umount_mutex);
|
||||
if (freed >= nr)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
return freed;
|
||||
}
|
||||
|
||||
void f2fs_join_shrinker(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
spin_lock(&f2fs_list_lock);
|
||||
list_add_tail(&sbi->s_list, &f2fs_list);
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
}
|
||||
|
||||
void f2fs_leave_shrinker(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
f2fs_shrink_extent_tree(sbi, __count_extent_cache(sbi));
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
list_del(&sbi->s_list);
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
}
|
1084
fs/f2fs/super.c
1084
fs/f2fs/super.c
File diff suppressed because it is too large
Load diff
161
fs/f2fs/trace.c
Normal file
161
fs/f2fs/trace.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* f2fs IO tracer
|
||||
*
|
||||
* Copyright (c) 2014 Motorola Mobility
|
||||
* Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/radix-tree.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "trace.h"
|
||||
|
||||
static RADIX_TREE(pids, GFP_ATOMIC);
|
||||
static spinlock_t pids_lock;
|
||||
static struct last_io_info last_io;
|
||||
|
||||
static inline void __print_last_io(void)
|
||||
{
|
||||
if (!last_io.len)
|
||||
return;
|
||||
|
||||
trace_printk("%3x:%3x %4x %-16s %2x %5x %12x %4x\n",
|
||||
last_io.major, last_io.minor,
|
||||
last_io.pid, "----------------",
|
||||
last_io.type,
|
||||
last_io.fio.rw,
|
||||
last_io.fio.new_blkaddr,
|
||||
last_io.len);
|
||||
memset(&last_io, 0, sizeof(last_io));
|
||||
}
|
||||
|
||||
static int __file_type(struct inode *inode, pid_t pid)
|
||||
{
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
return __ATOMIC_FILE;
|
||||
else if (f2fs_is_volatile_file(inode))
|
||||
return __VOLATILE_FILE;
|
||||
else if (S_ISDIR(inode->i_mode))
|
||||
return __DIR_FILE;
|
||||
else if (inode->i_ino == F2FS_NODE_INO(F2FS_I_SB(inode)))
|
||||
return __NODE_FILE;
|
||||
else if (inode->i_ino == F2FS_META_INO(F2FS_I_SB(inode)))
|
||||
return __META_FILE;
|
||||
else if (pid)
|
||||
return __NORMAL_FILE;
|
||||
else
|
||||
return __MISC_FILE;
|
||||
}
|
||||
|
||||
void f2fs_trace_pid(struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
pid_t pid = task_pid_nr(current);
|
||||
void *p;
|
||||
|
||||
page->private = pid;
|
||||
|
||||
if (radix_tree_preload(GFP_NOFS))
|
||||
return;
|
||||
|
||||
spin_lock(&pids_lock);
|
||||
p = radix_tree_lookup(&pids, pid);
|
||||
if (p == current)
|
||||
goto out;
|
||||
if (p)
|
||||
radix_tree_delete(&pids, pid);
|
||||
|
||||
f2fs_radix_tree_insert(&pids, pid, current);
|
||||
|
||||
trace_printk("%3x:%3x %4x %-16s\n",
|
||||
MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev),
|
||||
pid, current->comm);
|
||||
out:
|
||||
spin_unlock(&pids_lock);
|
||||
radix_tree_preload_end();
|
||||
}
|
||||
|
||||
void f2fs_trace_ios(struct f2fs_io_info *fio, int flush)
|
||||
{
|
||||
struct inode *inode;
|
||||
pid_t pid;
|
||||
int major, minor;
|
||||
|
||||
if (flush) {
|
||||
__print_last_io();
|
||||
return;
|
||||
}
|
||||
|
||||
inode = fio->page->mapping->host;
|
||||
pid = page_private(fio->page);
|
||||
|
||||
major = MAJOR(inode->i_sb->s_dev);
|
||||
minor = MINOR(inode->i_sb->s_dev);
|
||||
|
||||
if (last_io.major == major && last_io.minor == minor &&
|
||||
last_io.pid == pid &&
|
||||
last_io.type == __file_type(inode, pid) &&
|
||||
last_io.fio.rw == fio->rw &&
|
||||
last_io.fio.new_blkaddr + last_io.len ==
|
||||
fio->new_blkaddr) {
|
||||
last_io.len++;
|
||||
return;
|
||||
}
|
||||
|
||||
__print_last_io();
|
||||
|
||||
last_io.major = major;
|
||||
last_io.minor = minor;
|
||||
last_io.pid = pid;
|
||||
last_io.type = __file_type(inode, pid);
|
||||
last_io.fio = *fio;
|
||||
last_io.len = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
void f2fs_build_trace_ios(void)
|
||||
{
|
||||
spin_lock_init(&pids_lock);
|
||||
}
|
||||
|
||||
#define PIDVEC_SIZE 128
|
||||
static unsigned int gang_lookup_pids(pid_t *results, unsigned long first_index,
|
||||
unsigned int max_items)
|
||||
{
|
||||
struct radix_tree_iter iter;
|
||||
void **slot;
|
||||
unsigned int ret = 0;
|
||||
|
||||
if (unlikely(!max_items))
|
||||
return 0;
|
||||
|
||||
radix_tree_for_each_slot(slot, &pids, &iter, first_index) {
|
||||
results[ret] = iter.index;
|
||||
if (++ret == PIDVEC_SIZE)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void f2fs_destroy_trace_ios(void)
|
||||
{
|
||||
pid_t pid[PIDVEC_SIZE];
|
||||
pid_t next_pid = 0;
|
||||
unsigned int found;
|
||||
|
||||
spin_lock(&pids_lock);
|
||||
while ((found = gang_lookup_pids(pid, next_pid, PIDVEC_SIZE))) {
|
||||
unsigned idx;
|
||||
|
||||
next_pid = pid[found - 1] + 1;
|
||||
for (idx = 0; idx < found; idx++)
|
||||
radix_tree_delete(&pids, pid[idx]);
|
||||
}
|
||||
spin_unlock(&pids_lock);
|
||||
}
|
46
fs/f2fs/trace.h
Normal file
46
fs/f2fs/trace.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* f2fs IO tracer
|
||||
*
|
||||
* Copyright (c) 2014 Motorola Mobility
|
||||
* Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __F2FS_TRACE_H__
|
||||
#define __F2FS_TRACE_H__
|
||||
|
||||
#ifdef CONFIG_F2FS_IO_TRACE
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
enum file_type {
|
||||
__NORMAL_FILE,
|
||||
__DIR_FILE,
|
||||
__NODE_FILE,
|
||||
__META_FILE,
|
||||
__ATOMIC_FILE,
|
||||
__VOLATILE_FILE,
|
||||
__MISC_FILE,
|
||||
};
|
||||
|
||||
struct last_io_info {
|
||||
int major, minor;
|
||||
pid_t pid;
|
||||
enum file_type type;
|
||||
struct f2fs_io_info fio;
|
||||
block_t len;
|
||||
};
|
||||
|
||||
extern void f2fs_trace_pid(struct page *);
|
||||
extern void f2fs_trace_ios(struct f2fs_io_info *, int);
|
||||
extern void f2fs_build_trace_ios(void);
|
||||
extern void f2fs_destroy_trace_ios(void);
|
||||
#else
|
||||
#define f2fs_trace_pid(p)
|
||||
#define f2fs_trace_ios(i, n)
|
||||
#define f2fs_build_trace_ios()
|
||||
#define f2fs_destroy_trace_ios()
|
||||
|
||||
#endif
|
||||
#endif /* __F2FS_TRACE_H__ */
|
|
@ -83,7 +83,7 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name,
|
|||
}
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return f2fs_getxattr(dentry->d_inode, type, name, buffer, size);
|
||||
return f2fs_getxattr(d_inode(dentry), type, name, buffer, size, NULL);
|
||||
}
|
||||
|
||||
static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name,
|
||||
|
@ -108,7 +108,7 @@ static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name,
|
|||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return f2fs_setxattr(dentry->d_inode, type, name,
|
||||
return f2fs_setxattr(d_inode(dentry), type, name,
|
||||
value, size, NULL, flags);
|
||||
}
|
||||
|
||||
|
@ -130,19 +130,20 @@ static size_t f2fs_xattr_advise_list(struct dentry *dentry, char *list,
|
|||
static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*((char *)buffer) = F2FS_I(inode)->i_advise;
|
||||
if (buffer)
|
||||
*((char *)buffer) = F2FS_I(inode)->i_advise;
|
||||
return sizeof(char);
|
||||
}
|
||||
|
||||
static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags, int type)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
@ -152,6 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name,
|
|||
return -EINVAL;
|
||||
|
||||
F2FS_I(inode)->i_advise |= *(char *)value;
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -345,7 +347,8 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
|
||||
if (ipage) {
|
||||
inline_addr = inline_xattr_addr(ipage);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
set_page_dirty(ipage);
|
||||
} else {
|
||||
page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(page)) {
|
||||
|
@ -353,7 +356,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
return PTR_ERR(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);
|
||||
f2fs_put_page(page, 1);
|
||||
|
@ -374,7 +377,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
return PTR_ERR(xpage);
|
||||
}
|
||||
f2fs_bug_on(sbi, new_nid);
|
||||
f2fs_wait_on_page_writeback(xpage, NODE);
|
||||
f2fs_wait_on_page_writeback(xpage, NODE, true);
|
||||
} else {
|
||||
struct dnode_of_data dn;
|
||||
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
||||
|
@ -398,7 +401,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
}
|
||||
|
||||
int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
void *buffer, size_t buffer_size, struct page *ipage)
|
||||
{
|
||||
struct f2fs_xattr_entry *entry;
|
||||
void *base_addr;
|
||||
|
@ -412,7 +415,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
|||
if (len > F2FS_NAME_LEN)
|
||||
return -ERANGE;
|
||||
|
||||
base_addr = read_all_xattrs(inode, NULL);
|
||||
base_addr = read_all_xattrs(inode, ipage);
|
||||
if (!base_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -442,7 +445,7 @@ cleanup:
|
|||
|
||||
ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct f2fs_xattr_entry *entry;
|
||||
void *base_addr;
|
||||
int error = 0;
|
||||
|
@ -481,13 +484,12 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
const char *name, const void *value, size_t size,
|
||||
struct page *ipage, int flags)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct f2fs_xattr_entry *here, *last;
|
||||
void *base_addr;
|
||||
int found, newsize;
|
||||
size_t len;
|
||||
__u32 new_hsize;
|
||||
int error = -ENOMEM;
|
||||
int error = 0;
|
||||
|
||||
if (name == NULL)
|
||||
return -EINVAL;
|
||||
|
@ -497,12 +499,15 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
|
||||
len = strlen(name);
|
||||
|
||||
if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN(inode))
|
||||
if (len > F2FS_NAME_LEN)
|
||||
return -ERANGE;
|
||||
|
||||
if (size > MAX_VALUE_LEN(inode))
|
||||
return -E2BIG;
|
||||
|
||||
base_addr = read_all_xattrs(inode, ipage);
|
||||
if (!base_addr)
|
||||
goto exit;
|
||||
return -ENOMEM;
|
||||
|
||||
/* find entry with wanted name. */
|
||||
here = __find_xattr(base_addr, index, len, name);
|
||||
|
@ -535,7 +540,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
free = free + ENTRY_SIZE(here);
|
||||
|
||||
if (unlikely(free < newsize)) {
|
||||
error = -ENOSPC;
|
||||
error = -E2BIG;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +568,6 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
* Before we come here, old entry is removed.
|
||||
* We just write new entry.
|
||||
*/
|
||||
memset(last, 0, newsize);
|
||||
last->e_name_index = index;
|
||||
last->e_name_len = len;
|
||||
memcpy(last->e_name, name, len);
|
||||
|
@ -577,16 +581,15 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
if (error)
|
||||
goto exit;
|
||||
|
||||
if (is_inode_flag_set(fi, FI_ACL_MODE)) {
|
||||
inode->i_mode = fi->i_acl_mode;
|
||||
if (is_inode_flag_set(inode, FI_ACL_MODE)) {
|
||||
inode->i_mode = F2FS_I(inode)->i_acl_mode;
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
clear_inode_flag(fi, FI_ACL_MODE);
|
||||
clear_inode_flag(inode, FI_ACL_MODE);
|
||||
}
|
||||
|
||||
if (ipage)
|
||||
update_inode(inode, ipage);
|
||||
else
|
||||
update_inode_page(inode);
|
||||
if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
|
||||
!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
exit:
|
||||
kzfree(base_addr);
|
||||
return error;
|
||||
|
@ -603,7 +606,7 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
|
|||
if (ipage)
|
||||
return __f2fs_setxattr(inode, index, name, value,
|
||||
size, ipage, flags);
|
||||
f2fs_balance_fs(sbi);
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
/* protect xattr_ver */
|
||||
|
@ -612,5 +615,6 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
|
|||
up_write(&F2FS_I(inode)->i_sem);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
#define F2FS_XATTR_INDEX_LUSTRE 5
|
||||
#define F2FS_XATTR_INDEX_SECURITY 6
|
||||
#define F2FS_XATTR_INDEX_ADVISE 7
|
||||
/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */
|
||||
#define F2FS_XATTR_INDEX_ENCRYPTION 9
|
||||
|
||||
#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
|
||||
|
||||
struct f2fs_xattr_header {
|
||||
__le32 h_magic; /* magic number for identification */
|
||||
|
@ -115,18 +119,21 @@ extern const struct xattr_handler *f2fs_xattr_handlers[];
|
|||
|
||||
extern int f2fs_setxattr(struct inode *, int, const char *,
|
||||
const void *, size_t, struct page *, int);
|
||||
extern int f2fs_getxattr(struct inode *, int, const char *, void *, size_t);
|
||||
extern int f2fs_getxattr(struct inode *, int, const char *, void *,
|
||||
size_t, struct page *);
|
||||
extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
|
||||
#else
|
||||
|
||||
#define f2fs_xattr_handlers NULL
|
||||
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;
|
||||
}
|
||||
static inline int f2fs_getxattr(struct inode *inode, int index,
|
||||
const char *name, void *buffer, size_t buffer_size)
|
||||
const char *name, void *buffer,
|
||||
size_t buffer_size, struct page *dpage)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,9 @@ s32 bdev_check_bdi_valid(struct super_block *sb)
|
|||
fsi->prev_eio |= SDFAT_EIO_BDI;
|
||||
sdfat_log_msg(sb, KERN_ERR, "%s: block device is "
|
||||
"eliminated.(bdi:%p)", __func__, sb->s_bdi);
|
||||
#ifdef CONFIG_SDFAT_DEBUG
|
||||
sdfat_debug_warn_on(1);
|
||||
#endif
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -130,7 +132,9 @@ s32 bdev_mread(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 n
|
|||
if(!(fsi->prev_eio & SDFAT_EIO_READ)) {
|
||||
fsi->prev_eio |= SDFAT_EIO_READ;
|
||||
sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__);
|
||||
#ifdef CONFIG_SDFAT_DEBUG
|
||||
sdfat_debug_warn_on(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
|
@ -187,7 +191,9 @@ no_bh:
|
|||
if(!(fsi->prev_eio & SDFAT_EIO_WRITE)) {
|
||||
fsi->prev_eio |= SDFAT_EIO_WRITE;
|
||||
sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__);
|
||||
#ifdef CONFIG_SDFAT_DEBUG
|
||||
sdfat_debug_warn_on(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
|
|
|
@ -223,6 +223,8 @@ struct dentry_operations {
|
|||
|
||||
#define DCACHE_MAY_FREE 0x00800000
|
||||
|
||||
#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */
|
||||
|
||||
extern seqlock_t rename_lock;
|
||||
|
||||
/*
|
||||
|
@ -468,4 +470,61 @@ static inline unsigned long vfs_pressure_ratio(unsigned long val)
|
|||
{
|
||||
return mult_frac(val, sysctl_vfs_cache_pressure, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* d_inode - Get the actual inode of this dentry
|
||||
* @dentry: The dentry to query
|
||||
*
|
||||
* This is the helper normal filesystems should use to get at their own inodes
|
||||
* in their own dentries and ignore the layering superimposed upon them.
|
||||
*/
|
||||
static inline struct inode *d_inode(const struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_inode;
|
||||
}
|
||||
|
||||
/**
|
||||
* d_inode_rcu - Get the actual inode of this dentry with ACCESS_ONCE()
|
||||
* @dentry: The dentry to query
|
||||
*
|
||||
* This is the helper normal filesystems should use to get at their own inodes
|
||||
* in their own dentries and ignore the layering superimposed upon them.
|
||||
*/
|
||||
static inline struct inode *d_inode_rcu(const struct dentry *dentry)
|
||||
{
|
||||
return ACCESS_ONCE(dentry->d_inode);
|
||||
}
|
||||
|
||||
/**
|
||||
* d_backing_inode - Get upper or lower inode we should be using
|
||||
* @upper: The upper layer
|
||||
*
|
||||
* This is the helper that should be used to get at the inode that will be used
|
||||
* if this dentry were to be opened as a file. The inode may be on the upper
|
||||
* dentry or it may be on a lower dentry pinned by the upper.
|
||||
*
|
||||
* Normal filesystems should not use this to access their own inodes.
|
||||
*/
|
||||
static inline struct inode *d_backing_inode(const struct dentry *upper)
|
||||
{
|
||||
struct inode *inode = upper->d_inode;
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
/**
|
||||
* d_backing_dentry - Get upper or lower dentry we should be using
|
||||
* @upper: The upper layer
|
||||
*
|
||||
* This is the helper that should be used to get the dentry of the inode that
|
||||
* will be used if this dentry were opened as a file. It may be the upper
|
||||
* dentry or it may be a lower dentry pinned by the upper.
|
||||
*
|
||||
* Normal filesystems should not use this to access their own dentries.
|
||||
*/
|
||||
static inline struct dentry *d_backing_dentry(struct dentry *upper)
|
||||
{
|
||||
return upper;
|
||||
}
|
||||
|
||||
#endif /* __LINUX_DCACHE_H */
|
||||
|
|
|
@ -19,12 +19,16 @@
|
|||
#define F2FS_MAX_LOG_SECTOR_SIZE 12 /* 12 bits for 4096 bytes */
|
||||
#define F2FS_LOG_SECTORS_PER_BLOCK 3 /* log number for sector/blk */
|
||||
#define F2FS_BLKSIZE 4096 /* support only 4KB block */
|
||||
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
|
||||
#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 NEW_ADDR ((block_t)-1) /* used as block_t addresses */
|
||||
|
||||
#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS)
|
||||
#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS)
|
||||
|
||||
/* 0, 1(node nid), 2(meta nid) are reserved node id */
|
||||
#define F2FS_RESERVED_NODE_NUM 3
|
||||
|
||||
|
@ -33,7 +37,8 @@
|
|||
#define F2FS_META_INO(sbi) (sbi->meta_ino_num)
|
||||
|
||||
/* This flag is used by node and meta inodes, and by recovery */
|
||||
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
|
||||
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
|
||||
#define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM)
|
||||
|
||||
/*
|
||||
* For further optimization on multi-head logs, on-disk layout supports maximum
|
||||
|
@ -45,6 +50,9 @@
|
|||
#define MAX_ACTIVE_NODE_LOGS 8
|
||||
#define MAX_ACTIVE_DATA_LOGS 8
|
||||
|
||||
#define VERSION_LEN 256
|
||||
#define MAX_VOLUME_NAME 512
|
||||
|
||||
/*
|
||||
* For superblock
|
||||
*/
|
||||
|
@ -77,15 +85,22 @@ struct f2fs_super_block {
|
|||
__le32 node_ino; /* node inode number */
|
||||
__le32 meta_ino; /* meta inode number */
|
||||
__u8 uuid[16]; /* 128-bit uuid for volume */
|
||||
__le16 volume_name[512]; /* volume name */
|
||||
__le16 volume_name[MAX_VOLUME_NAME]; /* volume name */
|
||||
__le32 extension_count; /* # of extensions below */
|
||||
__u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
|
||||
__le32 cp_payload;
|
||||
__u8 version[VERSION_LEN]; /* the kernel version */
|
||||
__u8 init_version[VERSION_LEN]; /* the initial kernel version */
|
||||
__le32 feature; /* defined features */
|
||||
__u8 encryption_level; /* versioning level for encryption */
|
||||
__u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
|
||||
__u8 reserved[871]; /* valid reserved region */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* For checkpoint
|
||||
*/
|
||||
#define CP_FASTBOOT_FLAG 0x00000020
|
||||
#define CP_FSCK_FLAG 0x00000010
|
||||
#define CP_ERROR_FLAG 0x00000008
|
||||
#define CP_COMPACT_SUM_FLAG 0x00000004
|
||||
|
@ -147,7 +162,7 @@ struct f2fs_orphan_block {
|
|||
*/
|
||||
struct f2fs_extent {
|
||||
__le32 fofs; /* start file offset of the extent */
|
||||
__le32 blk_addr; /* start block address of the extent */
|
||||
__le32 blk; /* start block address of the extent */
|
||||
__le32 len; /* lengh of the extent */
|
||||
} __packed;
|
||||
|
||||
|
@ -155,12 +170,12 @@ struct f2fs_extent {
|
|||
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
|
||||
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers 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 NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
||||
|
||||
#define ADDRS_PER_PAGE(page, fi) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK)
|
||||
#define ADDRS_PER_PAGE(page, inode) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK)
|
||||
|
||||
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
||||
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
||||
|
@ -170,14 +185,13 @@ struct f2fs_extent {
|
|||
|
||||
#define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */
|
||||
#define F2FS_INLINE_DATA 0x02 /* file inline data flag */
|
||||
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
|
||||
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
|
||||
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
|
||||
|
||||
#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
|
||||
F2FS_INLINE_XATTR_ADDRS - 1))
|
||||
|
||||
#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) -\
|
||||
sizeof(__le32) * (DEF_ADDRS_PER_INODE + \
|
||||
DEF_NIDS_PER_INODE - 1))
|
||||
|
||||
struct f2fs_inode {
|
||||
__le16 i_mode; /* file mode */
|
||||
__u8 i_advise; /* file hints */
|
||||
|
@ -225,6 +239,8 @@ enum {
|
|||
OFFSET_BIT_SHIFT
|
||||
};
|
||||
|
||||
#define OFFSET_BIT_MASK (0x07) /* (0x01 << OFFSET_BIT_SHIFT) - 1 */
|
||||
|
||||
struct node_footer {
|
||||
__le32 nid; /* node id */
|
||||
__le32 ino; /* inode nunmber */
|
||||
|
@ -329,7 +345,7 @@ struct f2fs_summary {
|
|||
|
||||
struct summary_footer {
|
||||
unsigned char entry_type; /* SUM_TYPE_XXX */
|
||||
__u32 check_sum; /* summary checksum */
|
||||
__le32 check_sum; /* summary checksum */
|
||||
} __packed;
|
||||
|
||||
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
|
||||
|
@ -342,6 +358,12 @@ struct summary_footer {
|
|||
sizeof(struct sit_journal_entry))
|
||||
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
|
||||
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
|
||||
* summary blocks
|
||||
|
@ -371,18 +393,28 @@ struct sit_journal {
|
|||
__u8 reserved[SIT_JOURNAL_RESERVED];
|
||||
} __packed;
|
||||
|
||||
/* 4KB-sized summary block structure */
|
||||
struct f2fs_summary_block {
|
||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
||||
struct f2fs_extra_info {
|
||||
__le64 kbytes_written;
|
||||
__u8 reserved[EXTRA_INFO_RESERVED];
|
||||
} __packed;
|
||||
|
||||
struct f2fs_journal {
|
||||
union {
|
||||
__le16 n_nats;
|
||||
__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 {
|
||||
struct nat_journal nat_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;
|
||||
} __packed;
|
||||
|
||||
|
@ -402,15 +434,25 @@ typedef __le32 f2fs_hash_t;
|
|||
|
||||
#define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS)
|
||||
|
||||
/* the number of dentry in a block */
|
||||
#define NR_DENTRY_IN_BLOCK 214
|
||||
|
||||
/* MAX level for dir lookup */
|
||||
#define MAX_DIR_HASH_DEPTH 63
|
||||
|
||||
/* MAX buckets in one level of dir */
|
||||
#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1))
|
||||
|
||||
/*
|
||||
* space utilization of regular dentry and inline dentry
|
||||
* regular dentry inline dentry
|
||||
* bitmap 1 * 27 = 27 1 * 23 = 23
|
||||
* reserved 1 * 3 = 3 1 * 7 = 7
|
||||
* dentry 11 * 214 = 2354 11 * 182 = 2002
|
||||
* filename 8 * 214 = 1712 8 * 182 = 1456
|
||||
* total 4096 3488
|
||||
*
|
||||
* Note: there are more reserved space in inline dentry than in regular
|
||||
* dentry, when converting inline dentry we should handle this carefully.
|
||||
*/
|
||||
#define NR_DENTRY_IN_BLOCK 214 /* the number of dentry in a block */
|
||||
#define SIZE_OF_DIR_ENTRY 11 /* by byte */
|
||||
#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
|
||||
BITS_PER_BYTE)
|
||||
|
@ -435,6 +477,24 @@ struct f2fs_dentry_block {
|
|||
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
|
||||
} __packed;
|
||||
|
||||
/* for inline dir */
|
||||
#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \
|
||||
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
|
||||
BITS_PER_BYTE + 1))
|
||||
#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \
|
||||
BITS_PER_BYTE - 1) / BITS_PER_BYTE)
|
||||
#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \
|
||||
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
|
||||
NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE))
|
||||
|
||||
/* inline directory entry structure */
|
||||
struct f2fs_inline_dentry {
|
||||
__u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE];
|
||||
__u8 reserved[INLINE_RESERVED_SIZE];
|
||||
struct f2fs_dir_entry dentry[NR_INLINE_DENTRY];
|
||||
__u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN];
|
||||
} __packed;
|
||||
|
||||
/* file types used in inode_info->flags */
|
||||
enum {
|
||||
F2FS_FT_UNKNOWN,
|
||||
|
@ -448,4 +508,6 @@ enum {
|
|||
F2FS_FT_MAX
|
||||
};
|
||||
|
||||
#define S_SHIFT 12
|
||||
|
||||
#endif /* _LINUX_F2FS_FS_H */
|
||||
|
|
|
@ -49,6 +49,8 @@ struct swap_info_struct;
|
|||
struct seq_file;
|
||||
struct workqueue_struct;
|
||||
struct iov_iter;
|
||||
struct fscrypt_info;
|
||||
struct fscrypt_operations;
|
||||
|
||||
extern void __init inode_init(void);
|
||||
extern void __init inode_init_early(void);
|
||||
|
@ -641,6 +643,10 @@ struct inode {
|
|||
struct hlist_head i_fsnotify_marks;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
struct fscrypt_info *i_crypt_info;
|
||||
#endif
|
||||
|
||||
void *i_private; /* fs or device private pointer */
|
||||
};
|
||||
|
||||
|
@ -1236,6 +1242,9 @@ struct super_block {
|
|||
const struct xattr_handler **s_xattr;
|
||||
|
||||
struct list_head s_inodes; /* all inodes */
|
||||
|
||||
const struct fscrypt_operations *s_cop;
|
||||
|
||||
struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */
|
||||
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
|
||||
struct block_device *s_bdev;
|
||||
|
|
435
include/linux/fscrypto.h
Normal file
435
include/linux/fscrypto.h
Normal file
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
* 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 <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_ablkcipher *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 (*key_prefix)(struct inode *, u8 **);
|
||||
int (*prepare_context)(struct inode *);
|
||||
int (*set_context)(struct inode *, const void *, size_t, void *);
|
||||
int (*dummy_context)(struct inode *);
|
||||
bool (*is_encrypted)(struct inode *);
|
||||
bool (*empty_dir)(struct inode *);
|
||||
unsigned (*max_namelen)(struct inode *);
|
||||
};
|
||||
|
||||
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
||||
{
|
||||
if (inode->i_sb->s_cop->dummy_context &&
|
||||
inode->i_sb->s_cop->dummy_context(inode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_contents_enc_mode(u32 mode)
|
||||
{
|
||||
return (mode == FS_ENCRYPTION_MODE_AES_256_XTS);
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_filenames_enc_mode(u32 mode)
|
||||
{
|
||||
return (mode == FS_ENCRYPTION_MODE_AES_256_CTS);
|
||||
}
|
||||
|
||||
static inline 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 *, gfp_t);
|
||||
extern void fscrypt_release_ctx(struct fscrypt_ctx *);
|
||||
extern struct page *fscrypt_encrypt_page(struct inode *, struct page *, gfp_t);
|
||||
extern int fscrypt_decrypt_page(struct page *);
|
||||
extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
|
||||
extern void fscrypt_pullback_bio_page(struct page **, bool);
|
||||
extern void fscrypt_restore_control_page(struct page *);
|
||||
extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t,
|
||||
unsigned int);
|
||||
/* policy.c */
|
||||
extern int fscrypt_process_policy(struct 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,
|
||||
gfp_t f)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_release_ctx(struct fscrypt_ctx *c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_notsupp_encrypt_page(struct inode *i,
|
||||
struct page *p, gfp_t f)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_decrypt_page(struct page *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_decrypt_bio_pages(struct fscrypt_ctx *c,
|
||||
struct bio *b)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_pullback_bio_page(struct page **p, bool b)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_restore_control_page(struct page *p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p,
|
||||
sector_t s, unsigned int f)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* policy.c */
|
||||
static inline int fscrypt_notsupp_process_policy(struct 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 */
|
|
@ -14,7 +14,12 @@
|
|||
{ NODE, "NODE" }, \
|
||||
{ DATA, "DATA" }, \
|
||||
{ META, "META" }, \
|
||||
{ META_FLUSH, "META_FLUSH" })
|
||||
{ META_FLUSH, "META_FLUSH" }, \
|
||||
{ INMEM, "INMEM" }, \
|
||||
{ INMEM_DROP, "INMEM_DROP" }, \
|
||||
{ INMEM_REVOKE, "INMEM_REVOKE" }, \
|
||||
{ IPU, "IN-PLACE" }, \
|
||||
{ OPU, "OUT-OF-PLACE" })
|
||||
|
||||
#define F2FS_BIO_MASK(t) (t & (READA | WRITE_FLUSH_FUA))
|
||||
#define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO))
|
||||
|
@ -72,10 +77,13 @@
|
|||
#define show_cpreason(type) \
|
||||
__print_symbolic(type, \
|
||||
{ CP_UMOUNT, "Umount" }, \
|
||||
{ CP_FASTBOOT, "Fastboot" }, \
|
||||
{ CP_SYNC, "Sync" }, \
|
||||
{ CP_RECOVERY, "Recovery" }, \
|
||||
{ CP_DISCARD, "Discard" })
|
||||
|
||||
struct victim_sel_policy;
|
||||
struct f2fs_map_blocks;
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__inode,
|
||||
|
||||
|
@ -148,14 +156,14 @@ DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter,
|
|||
|
||||
TRACE_EVENT(f2fs_sync_file_exit,
|
||||
|
||||
TP_PROTO(struct inode *inode, bool need_cp, int datasync, int ret),
|
||||
TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret),
|
||||
|
||||
TP_ARGS(inode, need_cp, datasync, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(bool, need_cp)
|
||||
__field(int, need_cp)
|
||||
__field(int, datasync)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
@ -190,7 +198,7 @@ TRACE_EVENT(f2fs_sync_fs,
|
|||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->dirty = F2FS_SB(sb)->s_dirty;
|
||||
__entry->dirty = is_sbi_flag_set(F2FS_SB(sb), SBI_IS_DIRTY);
|
||||
__entry->wait = wait;
|
||||
),
|
||||
|
||||
|
@ -440,71 +448,66 @@ TRACE_EVENT(f2fs_truncate_partial_nodes,
|
|||
__entry->err)
|
||||
);
|
||||
|
||||
TRACE_EVENT_CONDITION(f2fs_submit_page_bio,
|
||||
TRACE_EVENT(f2fs_map_blocks,
|
||||
TP_PROTO(struct inode *inode, struct f2fs_map_blocks *map, int ret),
|
||||
|
||||
TP_PROTO(struct page *page, sector_t blkaddr, int type),
|
||||
|
||||
TP_ARGS(page, blkaddr, type),
|
||||
|
||||
TP_CONDITION(page->mapping),
|
||||
TP_ARGS(inode, map, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(pgoff_t, index)
|
||||
__field(sector_t, blkaddr)
|
||||
__field(int, type)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->index = page->index;
|
||||
__entry->blkaddr = blkaddr;
|
||||
__entry->type = type;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
||||
"blkaddr = 0x%llx, bio_type = %s%s",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->blkaddr,
|
||||
show_bio_type(__entry->type))
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_get_data_block,
|
||||
TP_PROTO(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int ret),
|
||||
|
||||
TP_ARGS(inode, iblock, bh, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(sector_t, iblock)
|
||||
__field(sector_t, bh_start)
|
||||
__field(size_t, bh_size)
|
||||
__field(block_t, m_lblk)
|
||||
__field(block_t, m_pblk)
|
||||
__field(unsigned int, m_len)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->iblock = iblock;
|
||||
__entry->bh_start = bh->b_blocknr;
|
||||
__entry->bh_size = bh->b_size;
|
||||
__entry->m_lblk = map->m_lblk;
|
||||
__entry->m_pblk = map->m_pblk;
|
||||
__entry->m_len = map->m_len;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, "
|
||||
"start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d",
|
||||
"start blkaddr = 0x%llx, len = 0x%llx, err = %d",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long long)__entry->iblock,
|
||||
(unsigned long long)__entry->bh_start,
|
||||
(unsigned long long)__entry->bh_size,
|
||||
(unsigned long long)__entry->m_lblk,
|
||||
(unsigned long long)__entry->m_pblk,
|
||||
(unsigned long long)__entry->m_len,
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_background_gc,
|
||||
|
||||
TP_PROTO(struct super_block *sb, long wait_ms,
|
||||
unsigned int prefree, unsigned int free),
|
||||
|
||||
TP_ARGS(sb, wait_ms, prefree, free),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(long, wait_ms)
|
||||
__field(unsigned int, prefree)
|
||||
__field(unsigned int, free)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->wait_ms = wait_ms;
|
||||
__entry->prefree = prefree;
|
||||
__entry->free = free;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u",
|
||||
show_dev(__entry),
|
||||
__entry->wait_ms,
|
||||
__entry->prefree,
|
||||
__entry->free)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_get_victim,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, int gc_type,
|
||||
|
@ -656,35 +659,94 @@ TRACE_EVENT(f2fs_direct_IO_exit,
|
|||
__entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_reserve_new_block,
|
||||
TRACE_EVENT(f2fs_reserve_new_blocks,
|
||||
|
||||
TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node),
|
||||
TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node,
|
||||
blkcnt_t count),
|
||||
|
||||
TP_ARGS(inode, nid, ofs_in_node),
|
||||
TP_ARGS(inode, nid, ofs_in_node, count),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(nid_t, nid)
|
||||
__field(unsigned int, ofs_in_node)
|
||||
__field(blkcnt_t, count)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->nid = nid;
|
||||
__entry->ofs_in_node = ofs_in_node;
|
||||
__entry->count = count;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u",
|
||||
TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u, count = %llu",
|
||||
show_dev(__entry),
|
||||
(unsigned int)__entry->nid,
|
||||
__entry->ofs_in_node)
|
||||
__entry->ofs_in_node,
|
||||
(unsigned long long)__entry->count)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
TP_ARGS(page, fio),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(pgoff_t, index)
|
||||
__field(block_t, old_blkaddr)
|
||||
__field(block_t, new_blkaddr)
|
||||
__field(int, rw)
|
||||
__field(int, type)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->index = page->index;
|
||||
__entry->old_blkaddr = fio->old_blkaddr;
|
||||
__entry->new_blkaddr = fio->new_blkaddr;
|
||||
__entry->rw = fio->rw;
|
||||
__entry->type = fio->type;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
||||
"oldaddr = 0x%llx, newaddr = 0x%llx rw = %s%s, type = %s",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->old_blkaddr,
|
||||
(unsigned long long)__entry->new_blkaddr,
|
||||
show_bio_type(__entry->rw),
|
||||
show_block_type(__entry->type))
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_bio,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
TP_ARGS(page, fio),
|
||||
|
||||
TP_CONDITION(page->mapping)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
TP_ARGS(page, fio),
|
||||
|
||||
TP_CONDITION(page->mapping)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, rw, type, bio),
|
||||
TP_ARGS(sb, fio, bio),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
|
@ -696,8 +758,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
|||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->rw = rw;
|
||||
__entry->type = type;
|
||||
__entry->rw = fio->rw;
|
||||
__entry->type = fio->type;
|
||||
__entry->sector = bio->bi_iter.bi_sector;
|
||||
__entry->size = bio->bi_iter.bi_size;
|
||||
),
|
||||
|
@ -712,18 +774,20 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
|||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_write_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, rw, type, bio),
|
||||
TP_ARGS(sb, fio, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, rw, type, bio),
|
||||
TP_ARGS(sb, fio, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
@ -831,6 +895,13 @@ DEFINE_EVENT(f2fs__page, f2fs_writepage,
|
|||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_do_write_data_page,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
||||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_readpage,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
@ -852,6 +923,20 @@ DEFINE_EVENT(f2fs__page, f2fs_vm_page_mkwrite,
|
|||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_register_inmem_page,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
||||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
||||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_writepages,
|
||||
|
||||
TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type),
|
||||
|
@ -916,36 +1001,30 @@ TRACE_EVENT(f2fs_writepages,
|
|||
__entry->for_sync)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_submit_page_mbio,
|
||||
TRACE_EVENT(f2fs_readpages,
|
||||
|
||||
TP_PROTO(struct page *page, int rw, int type, block_t blk_addr),
|
||||
TP_PROTO(struct inode *inode, struct page *page, unsigned int nrpage),
|
||||
|
||||
TP_ARGS(page, rw, type, blk_addr),
|
||||
TP_ARGS(inode, page, nrpage),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(int, rw)
|
||||
__field(int, type)
|
||||
__field(pgoff_t, index)
|
||||
__field(block_t, block)
|
||||
__field(pgoff_t, start)
|
||||
__field(unsigned int, nrpage)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->rw = rw;
|
||||
__entry->type = type;
|
||||
__entry->index = page->index;
|
||||
__entry->block = blk_addr;
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->start = page->index;
|
||||
__entry->nrpage = nrpage;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, %s%s, %s, index = %lu, blkaddr = 0x%llx",
|
||||
TP_printk("dev = (%d,%d), ino = %lu, start = %lu nrpage = %u",
|
||||
show_dev_ino(__entry),
|
||||
show_bio_type(__entry->rw),
|
||||
show_block_type(__entry->type),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->block)
|
||||
(unsigned long)__entry->start,
|
||||
__entry->nrpage)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_write_checkpoint,
|
||||
|
@ -998,14 +1077,15 @@ TRACE_EVENT(f2fs_issue_discard,
|
|||
|
||||
TRACE_EVENT(f2fs_issue_flush,
|
||||
|
||||
TP_PROTO(struct super_block *sb, bool nobarrier, bool flush_merge),
|
||||
TP_PROTO(struct super_block *sb, unsigned int nobarrier,
|
||||
unsigned int flush_merge),
|
||||
|
||||
TP_ARGS(sb, nobarrier, flush_merge),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(bool, nobarrier)
|
||||
__field(bool, flush_merge)
|
||||
__field(unsigned int, nobarrier)
|
||||
__field(unsigned int, flush_merge)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
|
@ -1019,6 +1099,183 @@ TRACE_EVENT(f2fs_issue_flush,
|
|||
__entry->nobarrier ? "skip (nobarrier)" : "issue",
|
||||
__entry->flush_merge ? " with flush_merge" : "")
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_lookup_extent_tree_start,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int pgofs),
|
||||
|
||||
TP_ARGS(inode, pgofs),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, pgofs)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->pgofs = pgofs;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u",
|
||||
show_dev_ino(__entry),
|
||||
__entry->pgofs)
|
||||
);
|
||||
|
||||
TRACE_EVENT_CONDITION(f2fs_lookup_extent_tree_end,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int pgofs,
|
||||
struct extent_info *ei),
|
||||
|
||||
TP_ARGS(inode, pgofs, ei),
|
||||
|
||||
TP_CONDITION(ei),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, pgofs)
|
||||
__field(unsigned int, fofs)
|
||||
__field(u32, blk)
|
||||
__field(unsigned int, len)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->pgofs = pgofs;
|
||||
__entry->fofs = ei->fofs;
|
||||
__entry->blk = ei->blk;
|
||||
__entry->len = ei->len;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, "
|
||||
"ext_info(fofs: %u, blk: %u, len: %u)",
|
||||
show_dev_ino(__entry),
|
||||
__entry->pgofs,
|
||||
__entry->fofs,
|
||||
__entry->blk,
|
||||
__entry->len)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_update_extent_tree_range,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int pgofs, block_t blkaddr,
|
||||
unsigned int len),
|
||||
|
||||
TP_ARGS(inode, pgofs, blkaddr, len),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, pgofs)
|
||||
__field(u32, blk)
|
||||
__field(unsigned int, len)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->pgofs = pgofs;
|
||||
__entry->blk = blkaddr;
|
||||
__entry->len = len;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, "
|
||||
"blkaddr = %u, len = %u",
|
||||
show_dev_ino(__entry),
|
||||
__entry->pgofs,
|
||||
__entry->blk,
|
||||
__entry->len)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_shrink_extent_tree,
|
||||
|
||||
TP_PROTO(struct f2fs_sb_info *sbi, unsigned int node_cnt,
|
||||
unsigned int tree_cnt),
|
||||
|
||||
TP_ARGS(sbi, node_cnt, tree_cnt),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(unsigned int, node_cnt)
|
||||
__field(unsigned int, tree_cnt)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sbi->sb->s_dev;
|
||||
__entry->node_cnt = node_cnt;
|
||||
__entry->tree_cnt = tree_cnt;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u",
|
||||
show_dev(__entry),
|
||||
__entry->node_cnt,
|
||||
__entry->tree_cnt)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_destroy_extent_tree,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int node_cnt),
|
||||
|
||||
TP_ARGS(inode, node_cnt),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, node_cnt)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->node_cnt = node_cnt;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, destroyed: node_cnt = %u",
|
||||
show_dev_ino(__entry),
|
||||
__entry->node_cnt)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, s64 count),
|
||||
|
||||
TP_ARGS(sb, type, count),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(int, type)
|
||||
__field(s64, count)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->type = type;
|
||||
__entry->count = count;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), %s, dirty count = %lld",
|
||||
show_dev(__entry),
|
||||
show_file_type(__entry->type),
|
||||
__entry->count)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_enter,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, s64 count),
|
||||
|
||||
TP_ARGS(sb, type, count)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_exit,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, s64 count),
|
||||
|
||||
TP_ARGS(sb, type, count)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_F2FS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
|
|
@ -172,6 +172,24 @@ struct inodes_stat_t {
|
|||
|
||||
#define FS_IOC_INVAL_MAPPING _IO('f', 13)
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
|
1
mm/mmap.c
Normal file → Executable file
1
mm/mmap.c
Normal file → Executable file
|
@ -633,6 +633,7 @@ void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma,
|
|||
* immediately update the gap to the correct value. Finally we
|
||||
* rebalance the rbtree after all augmented values have been set.
|
||||
*/
|
||||
smp_mb();
|
||||
rb_link_node(&vma->vm_rb, rb_parent, rb_link);
|
||||
vma->rb_subtree_gap = 0;
|
||||
vma_gap_update(vma);
|
||||
|
|
2
net/netfilter/xt_qtaguid.c
Normal file → Executable file
2
net/netfilter/xt_qtaguid.c
Normal file → Executable file
|
@ -1935,7 +1935,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
|
|||
);
|
||||
f_count = atomic_long_read(
|
||||
&sock_tag_entry->socket->file->f_count);
|
||||
seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u "
|
||||
seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u "
|
||||
"f_count=%lu\n",
|
||||
sock_tag_entry->sk,
|
||||
sock_tag_entry->tag, uid,
|
||||
|
|
|
@ -13,83 +13,83 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main (int argc, char **argv)
|
||||
#include <string.h>
|
||||
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
|
||||
/*
|
||||
* Given a vmlinux file, overwrites bytes at given offset with hmac bytes, available in
|
||||
* hmac file.
|
||||
* Return 0, if Success
|
||||
* -1, if Error
|
||||
*/
|
||||
int
|
||||
update_crypto_hmac (const char * vmlinux_path, const char * hmac_path, unsigned long offset)
|
||||
{
|
||||
if (argc < 2)
|
||||
FILE * vmlinux_fp = NULL;
|
||||
FILE * hmac_fp = NULL;
|
||||
int i = 0, j = 0;
|
||||
unsigned char hmac[SHA256_DIGEST_SIZE];
|
||||
|
||||
if (!vmlinux_path || !hmac_path || !offset)
|
||||
{
|
||||
printf ("\nUsage : \n");
|
||||
printf ("fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
printf ("fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
printf ("\n");
|
||||
printf ("FIPS update_crypto_hmac : Invalid Params");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp ("-u", argv[1]))
|
||||
vmlinux_fp = fopen (vmlinux_path, "r+b");
|
||||
if (!vmlinux_fp)
|
||||
{
|
||||
unsigned long offset = 0;
|
||||
unsigned char * vmlinux_file = NULL;
|
||||
unsigned char * hmac_file = NULL;
|
||||
printf ("Unable to open vmlinux file ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc != 5)
|
||||
{
|
||||
printf ("\nUsage : \n");
|
||||
printf ("fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
printf ("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vmlinux_file = argv[2];
|
||||
hmac_file = argv[3];
|
||||
offset = atol(argv[4]);
|
||||
|
||||
if (!vmlinux_file || !hmac_file || !offset)
|
||||
{
|
||||
printf ("./fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
return -1;
|
||||
}
|
||||
hmac_fp = fopen (hmac_path, "rb");
|
||||
|
||||
return update_crypto_hmac (vmlinux_file, hmac_file, offset);
|
||||
}
|
||||
else if (!strcmp ("-g", argv[1]))
|
||||
if (!hmac_fp)
|
||||
{
|
||||
const char * in_file = NULL;
|
||||
const char * section_name = NULL;
|
||||
unsigned long offset = 0;
|
||||
unsigned long size = 0;
|
||||
const char * out_file = NULL;
|
||||
printf ("Unable to open hmac file ");
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc != 7)
|
||||
{
|
||||
printf ("\nUsage : \n");
|
||||
printf ("./fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
printf ("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
in_file = argv[2];
|
||||
section_name = argv[3];
|
||||
offset = atol(argv[4]);
|
||||
size = atol(argv[5]);
|
||||
out_file = argv[6];
|
||||
|
||||
if (!in_file || !section_name || !offset || !size || !out_file)
|
||||
{
|
||||
printf ("./fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return collect_crypto_bytes (in_file, section_name, offset, size, out_file);
|
||||
}
|
||||
else
|
||||
if (SHA256_DIGEST_SIZE != fread (&hmac, sizeof(unsigned char), SHA256_DIGEST_SIZE, hmac_fp))
|
||||
{
|
||||
printf ("\nUsage : \n");
|
||||
printf ("fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
printf ("fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
printf ("\n");
|
||||
printf ("Unable to read %d bytes from hmac file", SHA256_DIGEST_SIZE);
|
||||
fclose (hmac_fp);
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
#if 0
|
||||
printf ("Hash : ");
|
||||
for (i = 0; i < sizeof(hmac); i++)
|
||||
printf ("%02x ", hmac[i]);
|
||||
printf ("\n");
|
||||
|
||||
printf ("Offset : %ld", offset);
|
||||
#endif
|
||||
|
||||
if (fseek (vmlinux_fp, offset, SEEK_SET) != 0 )
|
||||
{
|
||||
printf ("Unable to seek into vmlinux file.");
|
||||
fclose (hmac_fp);
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SHA256_DIGEST_SIZE != fwrite (hmac, sizeof(unsigned char), SHA256_DIGEST_SIZE, vmlinux_fp))
|
||||
{
|
||||
printf ("Unable to write %d byte into vmlinux", SHA256_DIGEST_SIZE);
|
||||
fclose (hmac_fp);
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose (vmlinux_fp);
|
||||
fclose (hmac_fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -175,80 +175,85 @@ collect_crypto_bytes (const char * in_file, const char * section_name, unsigned
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
|
||||
/*
|
||||
* Given a vmlinux file, overwrites bytes at given offset with hmac bytes, available in
|
||||
* hmac file.
|
||||
* Return 0, if Success
|
||||
* -1, if Error
|
||||
*/
|
||||
int
|
||||
update_crypto_hmac (const char * vmlinux_path, const char * hmac_path, unsigned long offset)
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
FILE * vmlinux_fp = NULL;
|
||||
FILE * hmac_fp = NULL;
|
||||
int i = 0, j = 0;
|
||||
unsigned char hmac[SHA256_DIGEST_SIZE];
|
||||
|
||||
if (!vmlinux_path || !hmac_path || !offset)
|
||||
if (argc < 2)
|
||||
{
|
||||
printf ("FIPS update_crypto_hmac : Invalid Params");
|
||||
printf ("\nUsage : \n");
|
||||
printf ("fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
printf ("fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
printf ("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vmlinux_fp = fopen (vmlinux_path, "r+b");
|
||||
if (!vmlinux_fp)
|
||||
if (!strcmp ("-u", argv[1]))
|
||||
{
|
||||
printf ("Unable to open vmlinux file ");
|
||||
return -1;
|
||||
}
|
||||
unsigned long offset = 0;
|
||||
unsigned char * vmlinux_file = NULL;
|
||||
unsigned char * hmac_file = NULL;
|
||||
|
||||
hmac_fp = fopen (hmac_path, "rb");
|
||||
if (argc != 5)
|
||||
{
|
||||
printf ("\nUsage : \n");
|
||||
printf ("fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
printf ("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vmlinux_file = argv[2];
|
||||
hmac_file = argv[3];
|
||||
offset = atol(argv[4]);
|
||||
|
||||
if (!vmlinux_file || !hmac_file || !offset)
|
||||
{
|
||||
printf ("./fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!hmac_fp)
|
||||
return update_crypto_hmac (vmlinux_file, hmac_file, offset);
|
||||
}
|
||||
else if (!strcmp ("-g", argv[1]))
|
||||
{
|
||||
printf ("Unable to open hmac file ");
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
const char * in_file = NULL;
|
||||
const char * section_name = NULL;
|
||||
unsigned long offset = 0;
|
||||
unsigned long size = 0;
|
||||
const char * out_file = NULL;
|
||||
|
||||
if (SHA256_DIGEST_SIZE != fread (&hmac, sizeof(unsigned char), SHA256_DIGEST_SIZE, hmac_fp))
|
||||
if (argc != 7)
|
||||
{
|
||||
printf ("\nUsage : \n");
|
||||
printf ("./fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
printf ("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
in_file = argv[2];
|
||||
section_name = argv[3];
|
||||
offset = atol(argv[4]);
|
||||
size = atol(argv[5]);
|
||||
out_file = argv[6];
|
||||
|
||||
if (!in_file || !section_name || !offset || !size || !out_file)
|
||||
{
|
||||
printf ("./fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return collect_crypto_bytes (in_file, section_name, offset, size, out_file);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Unable to read %d bytes from hmac file", SHA256_DIGEST_SIZE);
|
||||
fclose (hmac_fp);
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
printf ("\nUsage : \n");
|
||||
printf ("fips_crypto_utils -u vmlinux_file hmac_file offset");
|
||||
printf ("fips_crypto_utils -g vmlinux_file section_name offset size out_file");
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf ("Hash : ");
|
||||
for (i = 0; i < sizeof(hmac); i++)
|
||||
printf ("%02x ", hmac[i]);
|
||||
printf ("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf ("Offset : %ld", offset);
|
||||
#endif
|
||||
|
||||
if (fseek (vmlinux_fp, offset, SEEK_SET) != 0 )
|
||||
{
|
||||
printf ("Unable to seek into vmlinux file.");
|
||||
fclose (hmac_fp);
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SHA256_DIGEST_SIZE != fwrite (hmac, sizeof(unsigned char), SHA256_DIGEST_SIZE, vmlinux_fp))
|
||||
{
|
||||
printf ("Unable to write %d byte into vmlinux", SHA256_DIGEST_SIZE);
|
||||
fclose (hmac_fp);
|
||||
fclose (vmlinux_fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose (vmlinux_fp);
|
||||
fclose (hmac_fp);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -512,6 +512,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
|
|||
!strcmp(sb->s_type->name, "sysfs") ||
|
||||
!strcmp(sb->s_type->name, "pstore") ||
|
||||
!strcmp(sb->s_type->name, "debugfs") ||
|
||||
!strcmp(sb->s_type->name, "f2fs") ||
|
||||
!strcmp(sb->s_type->name, "rootfs");
|
||||
}
|
||||
|
||||
|
|
18
sound/core/timer.c
Normal file → Executable file
18
sound/core/timer.c
Normal file → Executable file
|
@ -215,11 +215,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
|
|||
slave->slave_id == master->slave_id) {
|
||||
list_move_tail(&slave->open_list, &master->slave_list_head);
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
spin_lock(&master->timer->lock);
|
||||
slave->master = master;
|
||||
slave->timer = master->timer;
|
||||
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
|
||||
list_add_tail(&slave->active_list,
|
||||
&master->slave_active_head);
|
||||
spin_unlock(&master->timer->lock);
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
}
|
||||
}
|
||||
|
@ -346,15 +348,18 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|||
timer->hw.close)
|
||||
timer->hw.close(timer);
|
||||
/* remove slave links */
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
spin_lock(&timer->lock);
|
||||
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
|
||||
open_list) {
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
_snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
|
||||
list_move_tail(&slave->open_list, &snd_timer_slave_list);
|
||||
slave->master = NULL;
|
||||
slave->timer = NULL;
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
list_del_init(&slave->ack_list);
|
||||
list_del_init(&slave->active_list);
|
||||
}
|
||||
spin_unlock(&timer->lock);
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
mutex_unlock(®ister_mutex);
|
||||
}
|
||||
out:
|
||||
|
@ -441,9 +446,12 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
|
|||
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
|
||||
if (timeri->master)
|
||||
if (timeri->master && timeri->timer) {
|
||||
spin_lock(&timeri->timer->lock);
|
||||
list_add_tail(&timeri->active_list,
|
||||
&timeri->master->slave_active_head);
|
||||
spin_unlock(&timeri->timer->lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
return 1; /* delayed start */
|
||||
}
|
||||
|
@ -489,6 +497,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
|
|||
if (!keep_flag) {
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
|
||||
list_del_init(&timeri->ack_list);
|
||||
list_del_init(&timeri->active_list);
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
}
|
||||
goto __end;
|
||||
|
|
24
sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
Normal file → Executable file
24
sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
Normal file → Executable file
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
|
@ -1356,7 +1356,11 @@ static int msm_ds2_dap_handle_commands(u32 cmd, void *arg)
|
|||
int ret = 0, port_id = 0;
|
||||
int32_t data;
|
||||
struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
|
||||
get_user(data, &dolby_data->data[0]);
|
||||
if (get_user(data, &dolby_data->data[0])) {
|
||||
pr_debug("%s error getting data\n", __func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_debug("%s: param_id %d,be_id %d,device_id 0x%x,length %d,data %d\n",
|
||||
__func__, dolby_data->param_id, dolby_data->be_id,
|
||||
|
@ -1471,11 +1475,23 @@ static int msm_ds2_dap_set_param(u32 cmd, void *arg)
|
|||
goto end;
|
||||
}
|
||||
|
||||
off = ds2_dap_params_offset[idx];
|
||||
if ((dolby_data->length <= 0) ||
|
||||
(dolby_data->length > TOTAL_LENGTH_DS2_PARAM - off)) {
|
||||
pr_err("%s: invalid length %d at idx %d\n",
|
||||
__func__, dolby_data->length, idx);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* cache the parameters */
|
||||
ds2_dap_params[cdev].dap_params_modified[idx] += 1;
|
||||
for (j = 0; j < dolby_data->length; j++) {
|
||||
off = ds2_dap_params_offset[idx];
|
||||
get_user(data, &dolby_data->data[j]);
|
||||
if (get_user(data, &dolby_data->data[j])) {
|
||||
pr_debug("%s:error getting data\n", __func__);
|
||||
rc = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
ds2_dap_params[cdev].params_val[off + j] = data;
|
||||
pr_debug("%s:off %d,val[i/p:o/p]-[%d / %d]\n",
|
||||
__func__, off, data,
|
||||
|
|
Loading…
Reference in a new issue