2013-11-10 23:13:19 +08:00
|
|
|
/*
|
|
|
|
* fs/f2fs/inline.c
|
|
|
|
* Copyright (c) 2013, Intel Corporation
|
|
|
|
* Authors: Huajun Li <huajun.li@intel.com>
|
|
|
|
* Haicheng Li <haicheng.li@intel.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"
|
2015-10-16 02:34:49 +08:00
|
|
|
#include "node.h"
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2015-04-24 01:27:21 +08:00
|
|
|
bool f2fs_may_inline_data(struct inode *inode)
|
2013-11-10 23:13:19 +08:00
|
|
|
{
|
2014-10-07 08:39:50 +08:00
|
|
|
if (f2fs_is_atomic_file(inode))
|
|
|
|
return false;
|
|
|
|
|
2015-03-19 13:23:48 +08:00
|
|
|
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))
|
2013-11-10 23:13:19 +08:00
|
|
|
return false;
|
|
|
|
|
2014-11-12 06:10:01 +08:00
|
|
|
if (i_size_read(inode) > MAX_INLINE_DATA)
|
|
|
|
return false;
|
|
|
|
|
2015-04-22 11:39:58 +08:00
|
|
|
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
|
|
|
return false;
|
|
|
|
|
2013-11-10 23:13:19 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-24 01:27:21 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
void read_inline_data(struct page *page, struct page *ipage)
|
2013-11-10 23:13:19 +08:00
|
|
|
{
|
|
|
|
void *src_addr, *dst_addr;
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
if (PageUptodate(page))
|
|
|
|
return;
|
2013-12-30 18:36:23 +08:00
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_bug_on(F2FS_P_SB(page), page->index);
|
2013-11-10 23:13:19 +08:00
|
|
|
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE);
|
2013-11-10 23:13:19 +08:00
|
|
|
|
|
|
|
/* Copy the whole inline data block */
|
|
|
|
src_addr = inline_data_addr(ipage);
|
2014-10-19 14:41:38 +08:00
|
|
|
dst_addr = kmap_atomic(page);
|
2013-11-10 23:13:19 +08:00
|
|
|
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
2014-10-27 13:59:27 +08:00
|
|
|
flush_dcache_page(page);
|
2014-10-19 14:41:38 +08:00
|
|
|
kunmap_atomic(dst_addr);
|
2013-11-10 23:13:19 +08:00
|
|
|
SetPageUptodate(page);
|
2014-10-24 10:48:09 +08:00
|
|
|
}
|
|
|
|
|
2015-03-10 13:16:25 +08:00
|
|
|
bool truncate_inline_inode(struct page *ipage, u64 from)
|
2015-01-26 20:22:55 +08:00
|
|
|
{
|
2015-03-10 13:16:25 +08:00
|
|
|
void *addr;
|
|
|
|
|
|
|
|
if (from >= MAX_INLINE_DATA)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
addr = inline_data_addr(ipage);
|
|
|
|
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
2015-03-10 13:16:25 +08:00
|
|
|
memset(addr + from, 0, MAX_INLINE_DATA - from);
|
2016-05-21 07:32:49 +08:00
|
|
|
set_page_dirty(ipage);
|
2015-03-10 13:16:25 +08:00
|
|
|
return true;
|
2015-01-26 20:22:55 +08:00
|
|
|
}
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
|
|
|
{
|
|
|
|
struct page *ipage;
|
|
|
|
|
|
|
|
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
|
|
|
if (IS_ERR(ipage)) {
|
|
|
|
unlock_page(page);
|
|
|
|
return PTR_ERR(ipage);
|
|
|
|
}
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
if (!f2fs_has_inline_data(inode)) {
|
|
|
|
f2fs_put_page(ipage, 1);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page->index)
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
zero_user_segment(page, 0, PAGE_SIZE);
|
2014-10-24 10:48:09 +08:00
|
|
|
else
|
|
|
|
read_inline_data(page, ipage);
|
|
|
|
|
|
|
|
SetPageUptodate(page);
|
|
|
|
f2fs_put_page(ipage, 1);
|
|
|
|
unlock_page(page);
|
2013-11-10 23:13:19 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
2013-11-10 23:13:19 +08:00
|
|
|
{
|
|
|
|
struct f2fs_io_info fio = {
|
2015-04-24 05:38:15 +08:00
|
|
|
.sbi = F2FS_I_SB(dn->inode),
|
2013-11-10 23:13:19 +08:00
|
|
|
.type = DATA,
|
|
|
|
.rw = WRITE_SYNC | REQ_PRIO,
|
2015-04-24 05:38:15 +08:00
|
|
|
.page = page,
|
2015-04-24 03:04:33 +08:00
|
|
|
.encrypted_page = NULL,
|
2013-11-10 23:13:19 +08:00
|
|
|
};
|
2014-11-26 03:34:02 +08:00
|
|
|
int dirty, err;
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
if (!f2fs_exist_data(dn->inode))
|
|
|
|
goto clear_out;
|
2014-08-19 05:41:11 +08:00
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
err = f2fs_reserve_block(dn, 0);
|
2014-04-16 13:22:50 +08:00
|
|
|
if (err)
|
2014-10-24 10:48:09 +08:00
|
|
|
return err;
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2016-02-03 10:26:13 +08:00
|
|
|
f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page));
|
2014-10-24 10:48:09 +08:00
|
|
|
|
2016-02-19 16:02:51 +08:00
|
|
|
read_inline_data(page, dn->inode_page);
|
2015-07-25 15:29:17 +08:00
|
|
|
set_page_dirty(page);
|
|
|
|
|
2014-11-26 03:34:02 +08:00
|
|
|
/* clear dirty state */
|
|
|
|
dirty = clear_page_dirty_for_io(page);
|
|
|
|
|
2013-11-10 23:13:19 +08:00
|
|
|
/* write data page to try to make data consistent */
|
|
|
|
set_page_writeback(page);
|
f2fs: trace old block address for CoWed page
This patch enables to trace old block address of CoWed page for better
debugging.
f2fs_submit_page_mbio: dev = (1,0), ino = 1, page_index = 0x1d4f0, oldaddr = 0xfe8ab, newaddr = 0xfee90 rw = WRITE_SYNC, type = NODE
f2fs_submit_page_mbio: dev = (1,0), ino = 1, page_index = 0x1d4f8, oldaddr = 0xfe8b0, newaddr = 0xfee91 rw = WRITE_SYNC, type = NODE
f2fs_submit_page_mbio: dev = (1,0), ino = 1, page_index = 0x1d4fa, oldaddr = 0xfe8ae, newaddr = 0xfee92 rw = WRITE_SYNC, type = NODE
f2fs_submit_page_mbio: dev = (1,0), ino = 134824, page_index = 0x96, oldaddr = 0xf049b, newaddr = 0x2bbe rw = WRITE, type = DATA
f2fs_submit_page_mbio: dev = (1,0), ino = 134824, page_index = 0x97, oldaddr = 0xf049c, newaddr = 0x2bbf rw = WRITE, type = DATA
f2fs_submit_page_mbio: dev = (1,0), ino = 134824, page_index = 0x98, oldaddr = 0xf049d, newaddr = 0x2bc0 rw = WRITE, type = DATA
f2fs_submit_page_mbio: dev = (1,0), ino = 135260, page_index = 0x47, oldaddr = 0xffffffff, newaddr = 0xf2631 rw = WRITE, type = DATA
f2fs_submit_page_mbio: dev = (1,0), ino = 135260, page_index = 0x48, oldaddr = 0xffffffff, newaddr = 0xf2632 rw = WRITE, type = DATA
f2fs_submit_page_mbio: dev = (1,0), ino = 135260, page_index = 0x49, oldaddr = 0xffffffff, newaddr = 0xf2633 rw = WRITE, type = DATA
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:36:38 +08:00
|
|
|
fio.old_blkaddr = dn->data_blkaddr;
|
2015-04-24 05:38:15 +08:00
|
|
|
write_data_page(dn, &fio);
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
2014-11-26 03:34:02 +08:00
|
|
|
if (dirty)
|
|
|
|
inode_dec_dirty_pages(dn->inode);
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2014-11-26 09:27:38 +08:00
|
|
|
/* this converted inline_data should be recovered. */
|
2016-05-21 01:13:22 +08:00
|
|
|
set_inode_flag(dn->inode, FI_APPEND_WRITE);
|
2014-11-26 09:27:38 +08:00
|
|
|
|
2013-11-10 23:13:19 +08:00
|
|
|
/* clear inline data and flag after data writeback */
|
2015-03-10 13:16:25 +08:00
|
|
|
truncate_inline_inode(dn->inode_page, 0);
|
2016-01-25 21:57:05 +08:00
|
|
|
clear_inline_node(dn->inode_page);
|
2014-10-24 10:48:09 +08:00
|
|
|
clear_out:
|
|
|
|
stat_dec_inline_inode(dn->inode);
|
2014-11-11 08:29:14 +08:00
|
|
|
f2fs_clear_inline_inode(dn->inode);
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_put_dnode(dn);
|
|
|
|
return 0;
|
2013-11-10 23:13:19 +08:00
|
|
|
}
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
int f2fs_convert_inline_inode(struct inode *inode)
|
2013-11-10 23:13:19 +08:00
|
|
|
{
|
2014-10-24 10:48:09 +08:00
|
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
|
|
|
struct dnode_of_data dn;
|
|
|
|
struct page *ipage, *page;
|
|
|
|
int err = 0;
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2015-12-23 03:09:35 +08:00
|
|
|
if (!f2fs_has_inline_data(inode))
|
|
|
|
return 0;
|
|
|
|
|
2016-04-30 07:11:53 +08:00
|
|
|
page = f2fs_grab_cache_page(inode->i_mapping, 0, false);
|
2014-10-24 10:48:09 +08:00
|
|
|
if (!page)
|
|
|
|
return -ENOMEM;
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_lock_op(sbi);
|
|
|
|
|
|
|
|
ipage = get_node_page(sbi, inode->i_ino);
|
|
|
|
if (IS_ERR(ipage)) {
|
2014-11-18 08:06:55 +08:00
|
|
|
err = PTR_ERR(ipage);
|
|
|
|
goto out;
|
2014-08-08 07:32:25 +08:00
|
|
|
}
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
set_new_dnode(&dn, inode, ipage, ipage, 0);
|
|
|
|
|
|
|
|
if (f2fs_has_inline_data(inode))
|
|
|
|
err = f2fs_convert_inline_page(&dn, page);
|
|
|
|
|
|
|
|
f2fs_put_dnode(&dn);
|
2014-11-18 08:06:55 +08:00
|
|
|
out:
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_unlock_op(sbi);
|
|
|
|
|
|
|
|
f2fs_put_page(page, 1);
|
2015-12-23 05:23:35 +08:00
|
|
|
|
2016-01-08 06:15:04 +08:00
|
|
|
f2fs_balance_fs(sbi, dn.node_changed);
|
2015-12-23 05:23:35 +08:00
|
|
|
|
2013-11-10 23:13:19 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
2013-11-10 23:13:19 +08:00
|
|
|
{
|
|
|
|
void *src_addr, *dst_addr;
|
|
|
|
struct dnode_of_data dn;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
|
|
|
err = get_dnode_of_data(&dn, 0, LOOKUP_NODE);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2014-10-16 01:16:54 +08:00
|
|
|
if (!f2fs_has_inline_data(inode)) {
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_put_dnode(&dn);
|
|
|
|
return -EAGAIN;
|
2014-10-16 01:16:54 +08:00
|
|
|
}
|
|
|
|
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_bug_on(F2FS_I_SB(inode), page->index);
|
|
|
|
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true);
|
2014-10-19 14:41:38 +08:00
|
|
|
src_addr = kmap_atomic(page);
|
2014-10-24 10:48:09 +08:00
|
|
|
dst_addr = inline_data_addr(dn.inode_page);
|
|
|
|
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
2014-10-19 14:41:38 +08:00
|
|
|
kunmap_atomic(src_addr);
|
2016-05-21 07:32:49 +08:00
|
|
|
set_page_dirty(dn.inode_page);
|
2013-11-10 23:13:19 +08:00
|
|
|
|
2016-05-21 01:13:22 +08:00
|
|
|
set_inode_flag(inode, FI_APPEND_WRITE);
|
|
|
|
set_inode_flag(inode, FI_DATA_EXIST);
|
2014-10-24 10:48:09 +08:00
|
|
|
|
2016-01-25 21:57:05 +08:00
|
|
|
clear_inline_node(dn.inode_page);
|
2013-11-10 23:13:19 +08:00
|
|
|
f2fs_put_dnode(&dn);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-12-26 11:49:48 +08:00
|
|
|
|
2014-08-08 07:57:17 +08:00
|
|
|
bool recover_inline_data(struct inode *inode, struct page *npage)
|
2013-12-26 11:49:48 +08:00
|
|
|
{
|
2014-09-03 06:31:18 +08:00
|
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
2013-12-26 11:49:48 +08:00
|
|
|
struct f2fs_inode *ri = NULL;
|
|
|
|
void *src_addr, *dst_addr;
|
|
|
|
struct page *ipage;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The inline_data recovery policy is as follows.
|
|
|
|
* [prev.] [next] of inline_data flag
|
|
|
|
* o o -> recover inline_data
|
|
|
|
* o x -> remove inline_data, and then recover data blocks
|
|
|
|
* x o -> remove inline_data, and then recover inline_data
|
|
|
|
* x x -> recover data blocks
|
|
|
|
*/
|
|
|
|
if (IS_INODE(npage))
|
|
|
|
ri = F2FS_INODE(npage);
|
|
|
|
|
|
|
|
if (f2fs_has_inline_data(inode) &&
|
2014-08-08 07:57:17 +08:00
|
|
|
ri && (ri->i_inline & F2FS_INLINE_DATA)) {
|
2013-12-26 11:49:48 +08:00
|
|
|
process_inline:
|
|
|
|
ipage = get_node_page(sbi, inode->i_ino);
|
2014-09-03 06:52:58 +08:00
|
|
|
f2fs_bug_on(sbi, IS_ERR(ipage));
|
2013-12-26 11:49:48 +08:00
|
|
|
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
2014-04-29 16:28:32 +08:00
|
|
|
|
2013-12-26 11:49:48 +08:00
|
|
|
src_addr = inline_data_addr(npage);
|
|
|
|
dst_addr = inline_data_addr(ipage);
|
|
|
|
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
2014-10-24 10:48:09 +08:00
|
|
|
|
2016-05-21 01:13:22 +08:00
|
|
|
set_inode_flag(inode, FI_INLINE_DATA);
|
|
|
|
set_inode_flag(inode, FI_DATA_EXIST);
|
2014-10-24 10:48:09 +08:00
|
|
|
|
2016-05-21 07:32:49 +08:00
|
|
|
set_page_dirty(ipage);
|
2013-12-26 11:49:48 +08:00
|
|
|
f2fs_put_page(ipage, 1);
|
2014-08-08 07:57:17 +08:00
|
|
|
return true;
|
2013-12-26 11:49:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (f2fs_has_inline_data(inode)) {
|
|
|
|
ipage = get_node_page(sbi, inode->i_ino);
|
2014-09-03 06:52:58 +08:00
|
|
|
f2fs_bug_on(sbi, IS_ERR(ipage));
|
2015-09-22 06:55:49 +08:00
|
|
|
if (!truncate_inline_inode(ipage, 0))
|
|
|
|
return false;
|
2014-10-24 10:48:09 +08:00
|
|
|
f2fs_clear_inline_inode(inode);
|
2013-12-26 11:49:48 +08:00
|
|
|
f2fs_put_page(ipage, 1);
|
2014-08-08 07:57:17 +08:00
|
|
|
} else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) {
|
2015-09-22 06:55:49 +08:00
|
|
|
if (truncate_blocks(inode, 0, false))
|
|
|
|
return false;
|
2013-12-26 11:49:48 +08:00
|
|
|
goto process_inline;
|
|
|
|
}
|
2014-08-08 07:57:17 +08:00
|
|
|
return false;
|
2013-12-26 11:49:48 +08:00
|
|
|
}
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
2015-05-16 07:26:10 +08:00
|
|
|
struct fscrypt_name *fname, struct page **res_page)
|
2014-09-24 18:17:53 +08:00
|
|
|
{
|
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
2014-10-14 08:26:14 +08:00
|
|
|
struct f2fs_inline_dentry *inline_dentry;
|
2015-04-28 08:12:39 +08:00
|
|
|
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
2014-09-24 18:17:53 +08:00
|
|
|
struct f2fs_dir_entry *de;
|
2014-10-19 13:52:52 +08:00
|
|
|
struct f2fs_dentry_ptr d;
|
2014-10-14 08:26:14 +08:00
|
|
|
struct page *ipage;
|
2015-04-28 08:12:39 +08:00
|
|
|
f2fs_hash_t namehash;
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
ipage = get_node_page(sbi, dir->i_ino);
|
2016-05-26 05:29:11 +08:00
|
|
|
if (IS_ERR(ipage)) {
|
|
|
|
*res_page = ipage;
|
2014-09-24 18:17:53 +08:00
|
|
|
return NULL;
|
2016-05-26 05:29:11 +08:00
|
|
|
}
|
2014-09-24 18:17:53 +08:00
|
|
|
|
2015-04-28 08:12:39 +08:00
|
|
|
namehash = f2fs_dentry_hash(&name);
|
|
|
|
|
2014-10-14 08:26:14 +08:00
|
|
|
inline_dentry = inline_data_addr(ipage);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
2015-04-28 07:26:24 +08:00
|
|
|
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
2015-04-28 08:12:39 +08:00
|
|
|
de = find_target_dentry(fname, namehash, NULL, &d);
|
2014-09-24 18:17:53 +08:00
|
|
|
unlock_page(ipage);
|
2014-10-14 08:26:14 +08:00
|
|
|
if (de)
|
|
|
|
*res_page = ipage;
|
|
|
|
else
|
|
|
|
f2fs_put_page(ipage, 0);
|
|
|
|
|
2014-09-24 18:17:53 +08:00
|
|
|
return de;
|
|
|
|
}
|
|
|
|
|
|
|
|
int make_empty_inline_dir(struct inode *inode, struct inode *parent,
|
|
|
|
struct page *ipage)
|
|
|
|
{
|
|
|
|
struct f2fs_inline_dentry *dentry_blk;
|
2014-10-19 14:06:41 +08:00
|
|
|
struct f2fs_dentry_ptr d;
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
dentry_blk = inline_data_addr(ipage);
|
|
|
|
|
2015-04-28 07:26:24 +08:00
|
|
|
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
2014-10-19 14:06:41 +08:00
|
|
|
do_make_empty_dir(inode, parent, &d);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
set_page_dirty(ipage);
|
|
|
|
|
|
|
|
/* update i_size to MAX_INLINE_DATA */
|
2016-05-21 07:32:49 +08:00
|
|
|
if (i_size_read(inode) < MAX_INLINE_DATA)
|
2016-05-21 00:22:03 +08:00
|
|
|
f2fs_i_size_write(inode, MAX_INLINE_DATA);
|
2014-09-24 18:17:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:14:06 +08:00
|
|
|
/*
|
|
|
|
* NOTE: ipage is grabbed by caller, but if any error occurs, we should
|
|
|
|
* release ipage in this function.
|
|
|
|
*/
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
|
2014-09-24 18:17:53 +08:00
|
|
|
struct f2fs_inline_dentry *inline_dentry)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
struct dnode_of_data dn;
|
|
|
|
struct f2fs_dentry_block *dentry_blk;
|
|
|
|
int err;
|
|
|
|
|
2016-04-30 07:11:53 +08:00
|
|
|
page = f2fs_grab_cache_page(dir->i_mapping, 0, false);
|
2015-07-14 18:14:06 +08:00
|
|
|
if (!page) {
|
|
|
|
f2fs_put_page(ipage, 1);
|
2014-09-24 18:17:53 +08:00
|
|
|
return -ENOMEM;
|
2015-07-14 18:14:06 +08:00
|
|
|
}
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
set_new_dnode(&dn, dir, ipage, NULL, 0);
|
|
|
|
err = f2fs_reserve_block(&dn, 0);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
2014-10-19 14:41:38 +08:00
|
|
|
dentry_blk = kmap_atomic(page);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
/* copy data from inline dentry block to new dentry block */
|
|
|
|
memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
|
|
|
|
INLINE_DENTRY_BITMAP_SIZE);
|
2015-08-24 17:36:25 +08:00
|
|
|
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.
|
|
|
|
*/
|
2014-09-24 18:17:53 +08:00
|
|
|
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);
|
|
|
|
|
2014-10-19 14:41:38 +08:00
|
|
|
kunmap_atomic(dentry_blk);
|
2014-09-24 18:17:53 +08:00
|
|
|
SetPageUptodate(page);
|
|
|
|
set_page_dirty(page);
|
|
|
|
|
|
|
|
/* clear inline dir and flag after data writeback */
|
2015-03-10 13:16:25 +08:00
|
|
|
truncate_inline_inode(ipage, 0);
|
2014-10-24 10:48:09 +08:00
|
|
|
|
2014-10-14 11:00:16 +08:00
|
|
|
stat_dec_inline_dir(dir);
|
2016-05-21 01:13:22 +08:00
|
|
|
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
2016-05-21 00:52:20 +08:00
|
|
|
f2fs_i_depth_write(dir, 1);
|
2016-05-21 07:32:49 +08:00
|
|
|
if (i_size_read(dir) < PAGE_SIZE)
|
2016-05-21 00:22:03 +08:00
|
|
|
f2fs_i_size_write(dir, PAGE_SIZE);
|
2014-09-24 18:17:53 +08:00
|
|
|
out:
|
|
|
|
f2fs_put_page(page, 1);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
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];
|
2016-04-27 22:22:20 +08:00
|
|
|
|
|
|
|
if (unlikely(!de->name_len)) {
|
|
|
|
bit_pos++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
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;
|
|
|
|
|
2016-04-30 06:16:42 +08:00
|
|
|
backup_dentry = f2fs_kmalloc(sizeof(struct f2fs_inline_dentry),
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
GFP_F2FS_ZERO);
|
2016-05-14 19:03:53 +08:00
|
|
|
if (!backup_dentry) {
|
|
|
|
f2fs_put_page(ipage, 1);
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
return -ENOMEM;
|
2016-05-14 19:03:53 +08:00
|
|
|
}
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
|
|
|
|
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);
|
2016-05-21 01:13:22 +08:00
|
|
|
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
kfree(backup_dentry);
|
|
|
|
return 0;
|
|
|
|
recover:
|
|
|
|
lock_page(ipage);
|
|
|
|
memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA);
|
2016-05-21 00:52:20 +08:00
|
|
|
f2fs_i_depth_write(dir, 0);
|
2016-05-21 00:22:03 +08:00
|
|
|
f2fs_i_size_write(dir, MAX_INLINE_DATA);
|
2016-05-21 07:32:49 +08:00
|
|
|
set_page_dirty(ipage);
|
f2fs: fix to convert inline directory correctly
With below serials, we will lose parts of dirents:
1) mount f2fs with inline_dentry option
2) echo 1 > /sys/fs/f2fs/sdX/dir_level
3) mkdir dir
4) touch 180 files named [1-180] in dir
5) touch 181 in dir
6) echo 3 > /proc/sys/vm/drop_caches
7) ll dir
ls: cannot access 2: No such file or directory
ls: cannot access 4: No such file or directory
ls: cannot access 5: No such file or directory
ls: cannot access 6: No such file or directory
ls: cannot access 8: No such file or directory
ls: cannot access 9: No such file or directory
...
total 360
drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./
drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../
-rw-r--r-- 1 root root 0 Feb 19 15:12 1
-rw-r--r-- 1 root root 0 Feb 19 15:12 10
-rw-r--r-- 1 root root 0 Feb 19 15:12 100
-????????? ? ? ? ? ? 101
-????????? ? ? ? ? ? 102
-????????? ? ? ? ? ? 103
...
The reason is: when doing the inline dir conversion, we didn't consider
that directory has hierarchical hash structure which can be configured
through sysfs interface 'dir_level'.
By default, dir_level of directory inode is 0, it means we have one bucket
in hash table located in first level, all dirents will be hashed in this
bucket, so it has no problem for us to do the duplication simply between
inline dentry page and converted normal dentry page.
However, if we configured dir_level with the value N (greater than 0), it
will expand the bucket number of first level hash table by 2^N - 1, it
hashs dirents into different buckets according their hash value, if we
still move all dirents to first bucket, it makes incorrent locating for
inline dirents, the result is, although we can iterate all dirents through
->readdir, we can't stat some of them in ->lookup which based on hash
table searching.
This patch fixes this issue by rehashing dirents into correct position
when converting inline directory.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-02-22 18:29:18 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-09-24 18:17:53 +08:00
|
|
|
int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
2015-03-31 06:07:16 +08:00
|
|
|
struct inode *inode, nid_t ino, umode_t mode)
|
2014-09-24 18:17:53 +08:00
|
|
|
{
|
|
|
|
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;
|
2015-02-16 16:17:20 +08:00
|
|
|
struct f2fs_dentry_ptr d;
|
2014-09-24 18:17:53 +08:00
|
|
|
int slots = GET_DENTRY_SLOTS(namelen);
|
2015-03-31 06:07:16 +08:00
|
|
|
struct page *page = NULL;
|
2014-09-24 18:17:53 +08:00
|
|
|
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);
|
2014-10-14 07:28:13 +08:00
|
|
|
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
|
|
|
|
slots, NR_INLINE_DENTRY);
|
2014-09-24 18:17:53 +08:00
|
|
|
if (bit_pos >= NR_INLINE_DENTRY) {
|
|
|
|
err = f2fs_convert_inline_dir(dir, ipage, dentry_blk);
|
2015-07-14 18:14:06 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = -EAGAIN;
|
2014-09-24 18:17:53 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-03-31 06:07:16 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-09-24 18:17:53 +08:00
|
|
|
}
|
2014-10-14 10:42:53 +08:00
|
|
|
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
2015-02-16 16:17:20 +08:00
|
|
|
|
|
|
|
name_hash = f2fs_dentry_hash(name);
|
2015-04-28 07:26:24 +08:00
|
|
|
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
2015-03-31 06:07:16 +08:00
|
|
|
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
|
2015-02-16 16:17:20 +08:00
|
|
|
|
2014-09-24 18:17:53 +08:00
|
|
|
set_page_dirty(ipage);
|
|
|
|
|
|
|
|
/* we don't need to mark_inode_dirty now */
|
2015-03-31 06:07:16 +08:00
|
|
|
if (inode) {
|
2016-05-21 00:52:20 +08:00
|
|
|
f2fs_i_pino_write(inode, dir->i_ino);
|
2015-03-31 06:07:16 +08:00
|
|
|
f2fs_put_page(page, 1);
|
|
|
|
}
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
update_parent_metadata(dir, inode, 0);
|
|
|
|
fail:
|
2015-03-31 06:07:16 +08:00
|
|
|
if (inode)
|
|
|
|
up_write(&F2FS_I(inode)->i_sem);
|
2014-09-24 18:17:53 +08:00
|
|
|
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);
|
2016-01-20 23:43:51 +08:00
|
|
|
f2fs_wait_on_page_writeback(page, NODE, true);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
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);
|
2016-06-02 12:18:25 +08:00
|
|
|
f2fs_put_page(page, 1);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
2016-05-21 00:52:20 +08:00
|
|
|
mark_inode_dirty_sync(dir);
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
if (inode)
|
2016-06-02 12:18:25 +08:00
|
|
|
f2fs_drop_nlink(dir, inode);
|
2014-09-24 18:17:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-04-28 07:26:24 +08:00
|
|
|
int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
|
2015-05-16 07:26:10 +08:00
|
|
|
struct fscrypt_str *fstr)
|
2014-09-24 18:17:53 +08:00
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
struct f2fs_inline_dentry *inline_dentry = NULL;
|
|
|
|
struct page *ipage = NULL;
|
2014-10-19 13:52:52 +08:00
|
|
|
struct f2fs_dentry_ptr d;
|
2014-09-24 18:17:53 +08:00
|
|
|
|
|
|
|
if (ctx->pos == NR_INLINE_DENTRY)
|
|
|
|
return 0;
|
|
|
|
|
2014-10-16 12:29:51 +08:00
|
|
|
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
2014-09-24 18:17:53 +08:00
|
|
|
if (IS_ERR(ipage))
|
|
|
|
return PTR_ERR(ipage);
|
|
|
|
|
|
|
|
inline_dentry = inline_data_addr(ipage);
|
|
|
|
|
2015-04-28 07:26:24 +08:00
|
|
|
make_dentry_ptr(inode, &d, (void *)inline_dentry, 2);
|
2014-10-19 13:52:52 +08:00
|
|
|
|
2015-04-28 07:26:24 +08:00
|
|
|
if (!f2fs_fill_dentries(ctx, &d, 0, fstr))
|
2014-10-16 12:29:51 +08:00
|
|
|
ctx->pos = NR_INLINE_DENTRY;
|
2014-09-24 18:17:53 +08:00
|
|
|
|
2014-10-16 12:29:51 +08:00
|
|
|
f2fs_put_page(ipage, 1);
|
2014-09-24 18:17:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-10-16 02:34:49 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|