2012-11-29 12:28:09 +08:00
|
|
|
/*
|
2012-11-02 16:10:40 +08:00
|
|
|
* fs/f2fs/inode.c
|
|
|
|
*
|
|
|
|
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
|
|
|
* http://www.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 <linux/buffer_head.h>
|
|
|
|
#include <linux/writeback.h>
|
|
|
|
|
|
|
|
#include "f2fs.h"
|
|
|
|
#include "node.h"
|
|
|
|
|
2013-04-20 00:28:40 +08:00
|
|
|
#include <trace/events/f2fs.h>
|
|
|
|
|
2012-11-02 16:10:40 +08:00
|
|
|
void f2fs_set_inode_flags(struct inode *inode)
|
|
|
|
{
|
|
|
|
unsigned int flags = F2FS_I(inode)->i_flags;
|
|
|
|
|
|
|
|
inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE |
|
|
|
|
S_NOATIME | S_DIRSYNC);
|
|
|
|
|
|
|
|
if (flags & FS_SYNC_FL)
|
|
|
|
inode->i_flags |= S_SYNC;
|
|
|
|
if (flags & FS_APPEND_FL)
|
|
|
|
inode->i_flags |= S_APPEND;
|
|
|
|
if (flags & FS_IMMUTABLE_FL)
|
|
|
|
inode->i_flags |= S_IMMUTABLE;
|
|
|
|
if (flags & FS_NOATIME_FL)
|
|
|
|
inode->i_flags |= S_NOATIME;
|
|
|
|
if (flags & FS_DIRSYNC_FL)
|
|
|
|
inode->i_flags |= S_DIRSYNC;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_read_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
|
|
|
struct f2fs_inode_info *fi = F2FS_I(inode);
|
|
|
|
struct page *node_page;
|
|
|
|
struct f2fs_node *rn;
|
|
|
|
struct f2fs_inode *ri;
|
|
|
|
|
|
|
|
/* Check if ino is within scope */
|
2013-03-17 16:27:20 +08:00
|
|
|
if (check_nid_range(sbi, inode->i_ino)) {
|
|
|
|
f2fs_msg(inode->i_sb, KERN_ERR, "bad inode number: %lu",
|
|
|
|
(unsigned long) inode->i_ino);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2012-11-02 16:10:40 +08:00
|
|
|
|
|
|
|
node_page = get_node_page(sbi, inode->i_ino);
|
|
|
|
if (IS_ERR(node_page))
|
|
|
|
return PTR_ERR(node_page);
|
|
|
|
|
2013-07-15 17:57:38 +08:00
|
|
|
rn = F2FS_NODE(node_page);
|
2012-11-02 16:10:40 +08:00
|
|
|
ri = &(rn->i);
|
|
|
|
|
|
|
|
inode->i_mode = le16_to_cpu(ri->i_mode);
|
|
|
|
i_uid_write(inode, le32_to_cpu(ri->i_uid));
|
|
|
|
i_gid_write(inode, le32_to_cpu(ri->i_gid));
|
|
|
|
set_nlink(inode, le32_to_cpu(ri->i_links));
|
|
|
|
inode->i_size = le64_to_cpu(ri->i_size);
|
|
|
|
inode->i_blocks = le64_to_cpu(ri->i_blocks);
|
|
|
|
|
|
|
|
inode->i_atime.tv_sec = le64_to_cpu(ri->i_atime);
|
|
|
|
inode->i_ctime.tv_sec = le64_to_cpu(ri->i_ctime);
|
|
|
|
inode->i_mtime.tv_sec = le64_to_cpu(ri->i_mtime);
|
|
|
|
inode->i_atime.tv_nsec = le32_to_cpu(ri->i_atime_nsec);
|
|
|
|
inode->i_ctime.tv_nsec = le32_to_cpu(ri->i_ctime_nsec);
|
|
|
|
inode->i_mtime.tv_nsec = le32_to_cpu(ri->i_mtime_nsec);
|
|
|
|
inode->i_generation = le32_to_cpu(ri->i_generation);
|
f2fs: save device node number into f2fs_inode
This patch stores inode->i_rdev into on-disk inode structure.
Alun reported that:
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # mknod mnt/sda1 b 8 1
aspire tmp # mknod mnt/null c 1 3
aspire tmp # mknod mnt/console c 5 1
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 5, 1 Jan 22 18:44 console
crw-r--r-- 1 root root 1, 3 Jan 22 18:44 null
brw-r--r-- 1 root root 8, 1 Jan 22 18:44 sda1
aspire tmp # umount mnt
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 console
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 null
brw-r--r-- 1 root root 0, 0 Jan 22 18:44 sda1
In this report, f2fs lost the major/minor numbers of device files after umount.
The reason was revealed that f2fs does not store the inode->i_rdev to the
on-disk inode data structure.
So, as the other file systems do, f2fs also stores i_rdev into the i_addr fields
in on-disk inode structure without any on-disk layout changes.
Note that, this bug is limited to device files made by mknod().
Reported-and-Tested-by: Alun Jones <alun.linux@ty-penguin.org.uk>
Signed-off-by: Changman Lee <cm224.lee@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2013-01-23 08:40:23 +08:00
|
|
|
if (ri->i_addr[0])
|
|
|
|
inode->i_rdev = old_decode_dev(le32_to_cpu(ri->i_addr[0]));
|
|
|
|
else
|
|
|
|
inode->i_rdev = new_decode_dev(le32_to_cpu(ri->i_addr[1]));
|
2012-11-02 16:10:40 +08:00
|
|
|
|
|
|
|
fi->i_current_depth = le32_to_cpu(ri->i_current_depth);
|
|
|
|
fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid);
|
|
|
|
fi->i_flags = le32_to_cpu(ri->i_flags);
|
|
|
|
fi->flags = 0;
|
|
|
|
fi->i_advise = ri->i_advise;
|
f2fs: fix tracking parent inode number
Previously, f2fs didn't track the parent inode number correctly which is stored
in each f2fs_inode. In the case of the following scenario, a bug can be occured.
Let's suppose there are one directory, "/b", and two files, "/a" and "/b/a".
- pino of "/a" is ROOT_INO.
- pino of "/b/a" is DIR_B_INO.
Then,
# sync
: The inode pages of "/a" and "/b/a" contain the parent inode numbers as
ROOT_INO and DIR_B_INO respectively.
# mv /a /b/a
: The parent inode number of "/a" should be changed to DIR_B_INO, but f2fs
didn't do that. Ref. f2fs_set_link().
In order to fix this clearly, I added i_pino in f2fs_inode_info, and whenever
it needs to be changed like in f2fs_add_link() and f2fs_set_link(), it is
updated temporarily in f2fs_inode_info.
And later, f2fs_write_inode() stores the latest information to the inode pages.
For power-off-recovery, f2fs_sync_file() triggers simply f2fs_write_inode().
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-12-10 16:52:48 +08:00
|
|
|
fi->i_pino = le32_to_cpu(ri->i_pino);
|
2012-11-02 16:10:40 +08:00
|
|
|
get_extent_info(&fi->ext, ri->i_ext);
|
|
|
|
f2fs_put_page(node_page, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
|
|
|
|
{
|
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
|
|
|
struct inode *inode;
|
2013-04-20 00:28:40 +08:00
|
|
|
int ret = 0;
|
2012-11-02 16:10:40 +08:00
|
|
|
|
|
|
|
inode = iget_locked(sb, ino);
|
|
|
|
if (!inode)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2013-04-20 00:28:40 +08:00
|
|
|
|
|
|
|
if (!(inode->i_state & I_NEW)) {
|
|
|
|
trace_f2fs_iget(inode);
|
2012-11-02 16:10:40 +08:00
|
|
|
return inode;
|
2013-04-20 00:28:40 +08:00
|
|
|
}
|
2012-11-02 16:10:40 +08:00
|
|
|
if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
|
|
|
|
goto make_now;
|
|
|
|
|
|
|
|
ret = do_read_inode(inode);
|
|
|
|
if (ret)
|
|
|
|
goto bad_inode;
|
|
|
|
make_now:
|
|
|
|
if (ino == F2FS_NODE_INO(sbi)) {
|
|
|
|
inode->i_mapping->a_ops = &f2fs_node_aops;
|
|
|
|
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
|
|
|
|
} else if (ino == F2FS_META_INO(sbi)) {
|
|
|
|
inode->i_mapping->a_ops = &f2fs_meta_aops;
|
|
|
|
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
|
|
|
|
} else if (S_ISREG(inode->i_mode)) {
|
|
|
|
inode->i_op = &f2fs_file_inode_operations;
|
|
|
|
inode->i_fop = &f2fs_file_operations;
|
|
|
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
|
|
|
} else if (S_ISDIR(inode->i_mode)) {
|
|
|
|
inode->i_op = &f2fs_dir_inode_operations;
|
|
|
|
inode->i_fop = &f2fs_dir_operations;
|
|
|
|
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
2013-05-20 15:15:22 +08:00
|
|
|
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
|
2012-11-02 16:10:40 +08:00
|
|
|
} else if (S_ISLNK(inode->i_mode)) {
|
|
|
|
inode->i_op = &f2fs_symlink_inode_operations;
|
|
|
|
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)) {
|
|
|
|
inode->i_op = &f2fs_special_inode_operations;
|
|
|
|
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
|
|
|
} else {
|
|
|
|
ret = -EIO;
|
|
|
|
goto bad_inode;
|
|
|
|
}
|
|
|
|
unlock_new_inode(inode);
|
2013-04-20 00:28:40 +08:00
|
|
|
trace_f2fs_iget(inode);
|
2012-11-02 16:10:40 +08:00
|
|
|
return inode;
|
|
|
|
|
|
|
|
bad_inode:
|
|
|
|
iget_failed(inode);
|
2013-04-20 00:28:40 +08:00
|
|
|
trace_f2fs_iget_exit(inode, ret);
|
2012-11-02 16:10:40 +08:00
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_inode(struct inode *inode, struct page *node_page)
|
|
|
|
{
|
|
|
|
struct f2fs_node *rn;
|
|
|
|
struct f2fs_inode *ri;
|
|
|
|
|
|
|
|
wait_on_page_writeback(node_page);
|
|
|
|
|
2013-07-15 17:57:38 +08:00
|
|
|
rn = F2FS_NODE(node_page);
|
2012-11-02 16:10:40 +08:00
|
|
|
ri = &(rn->i);
|
|
|
|
|
|
|
|
ri->i_mode = cpu_to_le16(inode->i_mode);
|
|
|
|
ri->i_advise = F2FS_I(inode)->i_advise;
|
|
|
|
ri->i_uid = cpu_to_le32(i_uid_read(inode));
|
|
|
|
ri->i_gid = cpu_to_le32(i_gid_read(inode));
|
|
|
|
ri->i_links = cpu_to_le32(inode->i_nlink);
|
|
|
|
ri->i_size = cpu_to_le64(i_size_read(inode));
|
|
|
|
ri->i_blocks = cpu_to_le64(inode->i_blocks);
|
|
|
|
set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext);
|
|
|
|
|
|
|
|
ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
|
|
|
|
ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
|
|
|
|
ri->i_mtime = cpu_to_le64(inode->i_mtime.tv_sec);
|
|
|
|
ri->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec);
|
|
|
|
ri->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
|
|
|
|
ri->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
|
|
|
|
ri->i_current_depth = cpu_to_le32(F2FS_I(inode)->i_current_depth);
|
|
|
|
ri->i_xattr_nid = cpu_to_le32(F2FS_I(inode)->i_xattr_nid);
|
|
|
|
ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags);
|
f2fs: fix tracking parent inode number
Previously, f2fs didn't track the parent inode number correctly which is stored
in each f2fs_inode. In the case of the following scenario, a bug can be occured.
Let's suppose there are one directory, "/b", and two files, "/a" and "/b/a".
- pino of "/a" is ROOT_INO.
- pino of "/b/a" is DIR_B_INO.
Then,
# sync
: The inode pages of "/a" and "/b/a" contain the parent inode numbers as
ROOT_INO and DIR_B_INO respectively.
# mv /a /b/a
: The parent inode number of "/a" should be changed to DIR_B_INO, but f2fs
didn't do that. Ref. f2fs_set_link().
In order to fix this clearly, I added i_pino in f2fs_inode_info, and whenever
it needs to be changed like in f2fs_add_link() and f2fs_set_link(), it is
updated temporarily in f2fs_inode_info.
And later, f2fs_write_inode() stores the latest information to the inode pages.
For power-off-recovery, f2fs_sync_file() triggers simply f2fs_write_inode().
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-12-10 16:52:48 +08:00
|
|
|
ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino);
|
2012-11-02 16:10:40 +08:00
|
|
|
ri->i_generation = cpu_to_le32(inode->i_generation);
|
f2fs: save device node number into f2fs_inode
This patch stores inode->i_rdev into on-disk inode structure.
Alun reported that:
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # mknod mnt/sda1 b 8 1
aspire tmp # mknod mnt/null c 1 3
aspire tmp # mknod mnt/console c 5 1
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 5, 1 Jan 22 18:44 console
crw-r--r-- 1 root root 1, 3 Jan 22 18:44 null
brw-r--r-- 1 root root 8, 1 Jan 22 18:44 sda1
aspire tmp # umount mnt
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 console
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 null
brw-r--r-- 1 root root 0, 0 Jan 22 18:44 sda1
In this report, f2fs lost the major/minor numbers of device files after umount.
The reason was revealed that f2fs does not store the inode->i_rdev to the
on-disk inode data structure.
So, as the other file systems do, f2fs also stores i_rdev into the i_addr fields
in on-disk inode structure without any on-disk layout changes.
Note that, this bug is limited to device files made by mknod().
Reported-and-Tested-by: Alun Jones <alun.linux@ty-penguin.org.uk>
Signed-off-by: Changman Lee <cm224.lee@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2013-01-23 08:40:23 +08:00
|
|
|
|
|
|
|
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
|
|
|
if (old_valid_dev(inode->i_rdev)) {
|
|
|
|
ri->i_addr[0] =
|
|
|
|
cpu_to_le32(old_encode_dev(inode->i_rdev));
|
|
|
|
ri->i_addr[1] = 0;
|
|
|
|
} else {
|
|
|
|
ri->i_addr[0] = 0;
|
|
|
|
ri->i_addr[1] =
|
|
|
|
cpu_to_le32(new_encode_dev(inode->i_rdev));
|
|
|
|
ri->i_addr[2] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
f2fs: fix handling errors got by f2fs_write_inode
Ruslan reported that f2fs hangs with an infinite loop in f2fs_sync_file():
while (sync_node_pages(sbi, inode->i_ino, &wbc) == 0)
f2fs_write_inode(inode, NULL);
The reason was revealed that the cold flag is not set even thought this inode is
a normal file. Therefore, sync_node_pages() skips to write node blocks since it
only writes cold node blocks.
The cold flag is stored to the node_footer in node block, and whenever a new
node page is allocated, it is set according to its file type, file or directory.
But, after sudden-power-off, when recovering the inode page, f2fs doesn't recover
its cold flag.
So, let's assign the cold flag in more right places.
One more thing:
If f2fs_write_inode() returns an error due to whatever situations, there would
be no dirty node pages so that sync_node_pages() returns zero.
(i.e., zero means nothing was written.)
Reported-by: Ruslan N. Marchenko <me@ruff.mobi>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-12-19 14:28:39 +08:00
|
|
|
set_cold_node(inode, node_page);
|
2012-11-02 16:10:40 +08:00
|
|
|
set_page_dirty(node_page);
|
2013-06-10 08:17:01 +08:00
|
|
|
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
|
2012-11-02 16:10:40 +08:00
|
|
|
}
|
|
|
|
|
f2fs: introduce a new global lock scheme
In the previous version, f2fs uses global locks according to the usage types,
such as directory operations, block allocation, block write, and so on.
Reference the following lock types in f2fs.h.
enum lock_type {
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
In that case, we lose the performance under the multi-threading environment,
since every types of operations must be conducted one at a time.
In order to address the problem, let's share the locks globally with a mutex
array regardless of any types.
So, let users grab a mutex and perform their jobs in parallel as much as
possbile.
For this, I propose a new global lock scheme as follows.
0. Data structure
- f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS]
- f2fs_sb_info -> node_write
1. mutex_lock_op(sbi)
- try to get an avaiable lock from the array.
- returns the index of the gottern lock variable.
2. mutex_unlock_op(sbi, index of the lock)
- unlock the given index of the lock.
3. mutex_lock_all(sbi)
- grab all the locks in the array before the checkpoint.
4. mutex_unlock_all(sbi)
- release all the locks in the array after checkpoint.
5. block_operations()
- call mutex_lock_all()
- sync_dirty_dir_inodes()
- grab node_write
- sync_node_pages()
Note that,
the pairs of mutex_lock_op()/mutex_unlock_op() and
mutex_lock_all()/mutex_unlock_all() should be used together.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-11-22 15:21:29 +08:00
|
|
|
int update_inode_page(struct inode *inode)
|
2012-11-02 16:10:40 +08:00
|
|
|
{
|
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
|
|
|
struct page *node_page;
|
2013-01-11 12:10:49 +08:00
|
|
|
|
2012-11-02 16:10:40 +08:00
|
|
|
node_page = get_node_page(sbi, inode->i_ino);
|
|
|
|
if (IS_ERR(node_page))
|
|
|
|
return PTR_ERR(node_page);
|
|
|
|
|
|
|
|
update_inode(inode, node_page);
|
|
|
|
f2fs_put_page(node_page, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
f2fs: introduce a new global lock scheme
In the previous version, f2fs uses global locks according to the usage types,
such as directory operations, block allocation, block write, and so on.
Reference the following lock types in f2fs.h.
enum lock_type {
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
In that case, we lose the performance under the multi-threading environment,
since every types of operations must be conducted one at a time.
In order to address the problem, let's share the locks globally with a mutex
array regardless of any types.
So, let users grab a mutex and perform their jobs in parallel as much as
possbile.
For this, I propose a new global lock scheme as follows.
0. Data structure
- f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS]
- f2fs_sb_info -> node_write
1. mutex_lock_op(sbi)
- try to get an avaiable lock from the array.
- returns the index of the gottern lock variable.
2. mutex_unlock_op(sbi, index of the lock)
- unlock the given index of the lock.
3. mutex_lock_all(sbi)
- grab all the locks in the array before the checkpoint.
4. mutex_unlock_all(sbi)
- release all the locks in the array after checkpoint.
5. block_operations()
- call mutex_lock_all()
- sync_dirty_dir_inodes()
- grab node_write
- sync_node_pages()
Note that,
the pairs of mutex_lock_op()/mutex_unlock_op() and
mutex_lock_all()/mutex_unlock_all() should be used together.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-11-22 15:21:29 +08:00
|
|
|
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|
|
|
{
|
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
|
|
|
int ret, ilock;
|
|
|
|
|
|
|
|
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
|
|
|
|
inode->i_ino == F2FS_META_INO(sbi))
|
|
|
|
return 0;
|
|
|
|
|
2013-06-10 08:17:01 +08:00
|
|
|
if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE))
|
|
|
|
return 0;
|
|
|
|
|
f2fs: introduce a new global lock scheme
In the previous version, f2fs uses global locks according to the usage types,
such as directory operations, block allocation, block write, and so on.
Reference the following lock types in f2fs.h.
enum lock_type {
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
In that case, we lose the performance under the multi-threading environment,
since every types of operations must be conducted one at a time.
In order to address the problem, let's share the locks globally with a mutex
array regardless of any types.
So, let users grab a mutex and perform their jobs in parallel as much as
possbile.
For this, I propose a new global lock scheme as follows.
0. Data structure
- f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS]
- f2fs_sb_info -> node_write
1. mutex_lock_op(sbi)
- try to get an avaiable lock from the array.
- returns the index of the gottern lock variable.
2. mutex_unlock_op(sbi, index of the lock)
- unlock the given index of the lock.
3. mutex_lock_all(sbi)
- grab all the locks in the array before the checkpoint.
4. mutex_unlock_all(sbi)
- release all the locks in the array after checkpoint.
5. block_operations()
- call mutex_lock_all()
- sync_dirty_dir_inodes()
- grab node_write
- sync_node_pages()
Note that,
the pairs of mutex_lock_op()/mutex_unlock_op() and
mutex_lock_all()/mutex_unlock_all() should be used together.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-11-22 15:21:29 +08:00
|
|
|
if (wbc)
|
|
|
|
f2fs_balance_fs(sbi);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to lock here to prevent from producing dirty node pages
|
|
|
|
* during the urgent cleaning time when runing out of free sections.
|
|
|
|
*/
|
|
|
|
ilock = mutex_lock_op(sbi);
|
|
|
|
ret = update_inode_page(inode);
|
|
|
|
mutex_unlock_op(sbi, ilock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-29 12:28:09 +08:00
|
|
|
/*
|
2012-11-02 16:10:40 +08:00
|
|
|
* Called at the last iput() if i_nlink is zero
|
|
|
|
*/
|
|
|
|
void f2fs_evict_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
f2fs: introduce a new global lock scheme
In the previous version, f2fs uses global locks according to the usage types,
such as directory operations, block allocation, block write, and so on.
Reference the following lock types in f2fs.h.
enum lock_type {
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
In that case, we lose the performance under the multi-threading environment,
since every types of operations must be conducted one at a time.
In order to address the problem, let's share the locks globally with a mutex
array regardless of any types.
So, let users grab a mutex and perform their jobs in parallel as much as
possbile.
For this, I propose a new global lock scheme as follows.
0. Data structure
- f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS]
- f2fs_sb_info -> node_write
1. mutex_lock_op(sbi)
- try to get an avaiable lock from the array.
- returns the index of the gottern lock variable.
2. mutex_unlock_op(sbi, index of the lock)
- unlock the given index of the lock.
3. mutex_lock_all(sbi)
- grab all the locks in the array before the checkpoint.
4. mutex_unlock_all(sbi)
- release all the locks in the array after checkpoint.
5. block_operations()
- call mutex_lock_all()
- sync_dirty_dir_inodes()
- grab node_write
- sync_node_pages()
Note that,
the pairs of mutex_lock_op()/mutex_unlock_op() and
mutex_lock_all()/mutex_unlock_all() should be used together.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-11-22 15:21:29 +08:00
|
|
|
int ilock;
|
2012-11-02 16:10:40 +08:00
|
|
|
|
2013-04-20 00:28:40 +08:00
|
|
|
trace_f2fs_evict_inode(inode);
|
2012-11-02 16:10:40 +08:00
|
|
|
truncate_inode_pages(&inode->i_data, 0);
|
|
|
|
|
|
|
|
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
|
|
|
|
inode->i_ino == F2FS_META_INO(sbi))
|
|
|
|
goto no_delete;
|
|
|
|
|
|
|
|
BUG_ON(atomic_read(&F2FS_I(inode)->dirty_dents));
|
|
|
|
remove_dirty_dir_inode(inode);
|
|
|
|
|
|
|
|
if (inode->i_nlink || is_bad_inode(inode))
|
|
|
|
goto no_delete;
|
|
|
|
|
2013-01-29 17:30:07 +08:00
|
|
|
sb_start_intwrite(inode->i_sb);
|
2012-11-02 16:10:40 +08:00
|
|
|
set_inode_flag(F2FS_I(inode), FI_NO_ALLOC);
|
|
|
|
i_size_write(inode, 0);
|
|
|
|
|
|
|
|
if (F2FS_HAS_BLOCKS(inode))
|
|
|
|
f2fs_truncate(inode);
|
|
|
|
|
f2fs: introduce a new global lock scheme
In the previous version, f2fs uses global locks according to the usage types,
such as directory operations, block allocation, block write, and so on.
Reference the following lock types in f2fs.h.
enum lock_type {
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
In that case, we lose the performance under the multi-threading environment,
since every types of operations must be conducted one at a time.
In order to address the problem, let's share the locks globally with a mutex
array regardless of any types.
So, let users grab a mutex and perform their jobs in parallel as much as
possbile.
For this, I propose a new global lock scheme as follows.
0. Data structure
- f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS]
- f2fs_sb_info -> node_write
1. mutex_lock_op(sbi)
- try to get an avaiable lock from the array.
- returns the index of the gottern lock variable.
2. mutex_unlock_op(sbi, index of the lock)
- unlock the given index of the lock.
3. mutex_lock_all(sbi)
- grab all the locks in the array before the checkpoint.
4. mutex_unlock_all(sbi)
- release all the locks in the array after checkpoint.
5. block_operations()
- call mutex_lock_all()
- sync_dirty_dir_inodes()
- grab node_write
- sync_node_pages()
Note that,
the pairs of mutex_lock_op()/mutex_unlock_op() and
mutex_lock_all()/mutex_unlock_all() should be used together.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-11-22 15:21:29 +08:00
|
|
|
ilock = mutex_lock_op(sbi);
|
2012-11-02 16:10:40 +08:00
|
|
|
remove_inode_page(inode);
|
f2fs: introduce a new global lock scheme
In the previous version, f2fs uses global locks according to the usage types,
such as directory operations, block allocation, block write, and so on.
Reference the following lock types in f2fs.h.
enum lock_type {
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
In that case, we lose the performance under the multi-threading environment,
since every types of operations must be conducted one at a time.
In order to address the problem, let's share the locks globally with a mutex
array regardless of any types.
So, let users grab a mutex and perform their jobs in parallel as much as
possbile.
For this, I propose a new global lock scheme as follows.
0. Data structure
- f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS]
- f2fs_sb_info -> node_write
1. mutex_lock_op(sbi)
- try to get an avaiable lock from the array.
- returns the index of the gottern lock variable.
2. mutex_unlock_op(sbi, index of the lock)
- unlock the given index of the lock.
3. mutex_lock_all(sbi)
- grab all the locks in the array before the checkpoint.
4. mutex_unlock_all(sbi)
- release all the locks in the array after checkpoint.
5. block_operations()
- call mutex_lock_all()
- sync_dirty_dir_inodes()
- grab node_write
- sync_node_pages()
Note that,
the pairs of mutex_lock_op()/mutex_unlock_op() and
mutex_lock_all()/mutex_unlock_all() should be used together.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-11-22 15:21:29 +08:00
|
|
|
mutex_unlock_op(sbi, ilock);
|
|
|
|
|
2013-01-29 17:30:07 +08:00
|
|
|
sb_end_intwrite(inode->i_sb);
|
2012-11-02 16:10:40 +08:00
|
|
|
no_delete:
|
|
|
|
clear_inode(inode);
|
|
|
|
}
|