Skip to content

Commit

Permalink
f2fs: restructure f2fs page.private layout
Browse files Browse the repository at this point in the history
[ Upstream commit b763f3b ]

Restruct f2fs page private layout for below reasons:

There are some cases that f2fs wants to set a flag in a page to
indicate a specified status of page:
a) page is in transaction list for atomic write
b) page contains dummy data for aligned write
c) page is migrating for GC
d) page contains inline data for inline inode flush
e) page belongs to merkle tree, and is verified for fsverity
f) page is dirty and has filesystem/inode reference count for writeback
g) page is temporary and has decompress io context reference for compression

There are existed places in page structure we can use to store
f2fs private status/data:
- page.flags: PG_checked, PG_private
- page.private

However it was a mess when we using them, which may cause potential
confliction:
		page.private	PG_private	PG_checked	page._refcount (+1 at most)
a)		-1		set				+1
b)		-2		set
c), d), e)					set
f)		0		set				+1
g)		pointer		set

The other problem is page.flags has no free slot, if we can avoid set
zero to page.private and set PG_private flag, then we use non-zero value
to indicate PG_private status, so that we may have chance to reclaim
PG_private slot for other usage. [1]

The other concern is f2fs has bad scalability in aspect of indicating
more page status.

So in this patch, let's restructure f2fs' page.private as below to
solve above issues:

Layout A: lowest bit should be 1
| bit0 = 1 | bit1 | bit2 | ... | bit MAX | private data .... |
 bit 0	PAGE_PRIVATE_NOT_POINTER
 bit 1	PAGE_PRIVATE_ATOMIC_WRITE
 bit 2	PAGE_PRIVATE_DUMMY_WRITE
 bit 3	PAGE_PRIVATE_ONGOING_MIGRATION
 bit 4	PAGE_PRIVATE_INLINE_INODE
 bit 5	PAGE_PRIVATE_REF_RESOURCE
 bit 6-	f2fs private data

Layout B: lowest bit should be 0
 page.private is a wrapped pointer.

After the change:
		page.private	PG_private	PG_checked	page._refcount (+1 at most)
a)		11		set				+1
b)		101		set				+1
c)		1001		set				+1
d)		10001		set				+1
e)						set
f)		100001		set				+1
g)		pointer		set				+1

[1] https://lore.kernel.org/linux-f2fs-devel/20210422154705.GO3596236@casper.infradead.org/T/#u

Cc: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
chaseyu authored and gregkh committed Sep 18, 2021
1 parent cd122dc commit 2040daf
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 109 deletions.
4 changes: 2 additions & 2 deletions fs/f2fs/checkpoint.c
Expand Up @@ -444,7 +444,7 @@ static int f2fs_set_meta_page_dirty(struct page *page)
if (!PageDirty(page)) {
__set_page_dirty_nobuffers(page);
inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META);
f2fs_set_page_private(page, 0);
set_page_private_reference(page);
return 1;
}
return 0;
Expand Down Expand Up @@ -1018,7 +1018,7 @@ void f2fs_update_dirty_page(struct inode *inode, struct page *page)
inode_inc_dirty_pages(inode);
spin_unlock(&sbi->inode_lock[type]);

f2fs_set_page_private(page, 0);
set_page_private_reference(page);
}

void f2fs_remove_dirty_inode(struct inode *inode)
Expand Down
10 changes: 4 additions & 6 deletions fs/f2fs/compress.c
Expand Up @@ -74,7 +74,7 @@ bool f2fs_is_compressed_page(struct page *page)
return false;
if (!page_private(page))
return false;
if (IS_ATOMIC_WRITTEN_PAGE(page) || IS_DUMMY_WRITTEN_PAGE(page))
if (page_private_nonpointer(page))
return false;

f2fs_bug_on(F2FS_M_SB(page->mapping),
Expand All @@ -85,8 +85,7 @@ bool f2fs_is_compressed_page(struct page *page)
static void f2fs_set_compressed_page(struct page *page,
struct inode *inode, pgoff_t index, void *data)
{
SetPagePrivate(page);
set_page_private(page, (unsigned long)data);
attach_page_private(page, (void *)data);

/* i_crypto_info and iv index */
page->index = index;
Expand Down Expand Up @@ -589,8 +588,7 @@ static void f2fs_compress_free_page(struct page *page)
{
if (!page)
return;
set_page_private(page, (unsigned long)NULL);
ClearPagePrivate(page);
detach_page_private(page);
page->mapping = NULL;
unlock_page(page);
mempool_free(page, compress_page_pool);
Expand Down Expand Up @@ -1399,7 +1397,7 @@ void f2fs_compress_write_end_io(struct bio *bio, struct page *page)

for (i = 0; i < cic->nr_rpages; i++) {
WARN_ON(!cic->rpages[i]);
clear_cold_data(cic->rpages[i]);
clear_page_private_gcing(cic->rpages[i]);
end_page_writeback(cic->rpages[i]);
}

Expand Down
65 changes: 37 additions & 28 deletions fs/f2fs/data.c
Expand Up @@ -58,18 +58,19 @@ static bool __is_cp_guaranteed(struct page *page)
if (!mapping)
return false;

if (f2fs_is_compressed_page(page))
return false;

inode = mapping->host;
sbi = F2FS_I_SB(inode);

if (inode->i_ino == F2FS_META_INO(sbi) ||
inode->i_ino == F2FS_NODE_INO(sbi) ||
S_ISDIR(inode->i_mode) ||
(S_ISREG(inode->i_mode) &&
S_ISDIR(inode->i_mode))
return true;

if (f2fs_is_compressed_page(page))
return false;
if ((S_ISREG(inode->i_mode) &&
(f2fs_is_atomic_file(inode) || IS_NOQUOTA(inode))) ||
is_cold_data(page))
page_private_gcing(page))
return true;
return false;
}
Expand Down Expand Up @@ -299,9 +300,8 @@ static void f2fs_write_end_io(struct bio *bio)
struct page *page = bvec->bv_page;
enum count_type type = WB_DATA_TYPE(page);

if (IS_DUMMY_WRITTEN_PAGE(page)) {
set_page_private(page, (unsigned long)NULL);
ClearPagePrivate(page);
if (page_private_dummy(page)) {
clear_page_private_dummy(page);
unlock_page(page);
mempool_free(page, sbi->write_io_dummy);

Expand Down Expand Up @@ -331,7 +331,7 @@ static void f2fs_write_end_io(struct bio *bio)
dec_page_count(sbi, type);
if (f2fs_in_warm_node_list(sbi, page))
f2fs_del_fsync_node_entry(sbi, page);
clear_cold_data(page);
clear_page_private_gcing(page);
end_page_writeback(page);
}
if (!get_pages(sbi, F2FS_WB_CP_DATA) &&
Expand Down Expand Up @@ -455,10 +455,11 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi,
GFP_NOIO | __GFP_NOFAIL);
f2fs_bug_on(sbi, !page);

zero_user_segment(page, 0, PAGE_SIZE);
SetPagePrivate(page);
set_page_private(page, DUMMY_WRITTEN_PAGE);
lock_page(page);

zero_user_segment(page, 0, PAGE_SIZE);
set_page_private_dummy(page);

if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE)
f2fs_bug_on(sbi, 1);
}
Expand Down Expand Up @@ -2482,9 +2483,9 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
if (f2fs_is_atomic_file(inode))
return true;
if (fio) {
if (is_cold_data(fio->page))
if (page_private_gcing(fio->page))
return true;
if (IS_ATOMIC_WRITTEN_PAGE(fio->page))
if (page_private_dummy(fio->page))
return true;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
f2fs_is_checkpointed_data(sbi, fio->old_blkaddr)))
Expand Down Expand Up @@ -2540,7 +2541,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
/* This page is already truncated */
if (fio->old_blkaddr == NULL_ADDR) {
ClearPageUptodate(page);
clear_cold_data(page);
clear_page_private_gcing(page);
goto out_writepage;
}
got_it:
Expand Down Expand Up @@ -2750,7 +2751,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
inode_dec_dirty_pages(inode);
if (err) {
ClearPageUptodate(page);
clear_cold_data(page);
clear_page_private_gcing(page);
}

if (wbc->for_reclaim) {
Expand Down Expand Up @@ -3224,7 +3225,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
f2fs_do_read_inline_data(page, ipage);
set_inode_flag(inode, FI_DATA_EXIST);
if (inode->i_nlink)
set_inline_node(ipage);
set_page_private_inline(ipage);
} else {
err = f2fs_convert_inline_page(&dn, page);
if (err)
Expand Down Expand Up @@ -3615,12 +3616,13 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset,
}
}

clear_cold_data(page);
clear_page_private_gcing(page);

if (IS_ATOMIC_WRITTEN_PAGE(page))
if (page_private_atomic(page))
return f2fs_drop_inmem_page(inode, page);

f2fs_clear_page_private(page);
detach_page_private(page);
set_page_private(page, 0);
}

int f2fs_release_page(struct page *page, gfp_t wait)
Expand All @@ -3630,11 +3632,13 @@ int f2fs_release_page(struct page *page, gfp_t wait)
return 0;

/* This is atomic written page, keep Private */
if (IS_ATOMIC_WRITTEN_PAGE(page))
if (page_private_atomic(page))
return 0;

clear_cold_data(page);
f2fs_clear_page_private(page);
clear_page_private_gcing(page);

detach_page_private(page);
set_page_private(page, 0);
return 1;
}

Expand All @@ -3650,7 +3654,7 @@ static int f2fs_set_data_page_dirty(struct page *page)
return __set_page_dirty_nobuffers(page);

if (f2fs_is_atomic_file(inode) && !f2fs_is_commit_atomic_write(inode)) {
if (!IS_ATOMIC_WRITTEN_PAGE(page)) {
if (!page_private_atomic(page)) {
f2fs_register_inmem_page(inode, page);
return 1;
}
Expand Down Expand Up @@ -3742,7 +3746,7 @@ int f2fs_migrate_page(struct address_space *mapping,
{
int rc, extra_count;
struct f2fs_inode_info *fi = F2FS_I(mapping->host);
bool atomic_written = IS_ATOMIC_WRITTEN_PAGE(page);
bool atomic_written = page_private_atomic(page);

BUG_ON(PageWriteback(page));

Expand Down Expand Up @@ -3778,8 +3782,13 @@ int f2fs_migrate_page(struct address_space *mapping,
}

if (PagePrivate(page)) {
f2fs_set_page_private(newpage, page_private(page));
f2fs_clear_page_private(page);
set_page_private(newpage, page_private(page));
SetPagePrivate(newpage);
get_page(newpage);

set_page_private(page, 0);
ClearPagePrivate(page);
put_page(page);
}

if (mode != MIGRATE_SYNC_NO_COPY)
Expand Down
8 changes: 6 additions & 2 deletions fs/f2fs/dir.c
Expand Up @@ -929,11 +929,15 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
!f2fs_truncate_hole(dir, page->index, page->index + 1)) {
f2fs_clear_page_cache_dirty_tag(page);
clear_page_dirty_for_io(page);
f2fs_clear_page_private(page);
ClearPageUptodate(page);
clear_cold_data(page);

clear_page_private_gcing(page);

inode_dec_dirty_pages(dir);
f2fs_remove_dirty_inode(dir);

detach_page_private(page);
set_page_private(page, 0);
}
f2fs_put_page(page, 1);

Expand Down
98 changes: 76 additions & 22 deletions fs/f2fs/f2fs.h
Expand Up @@ -1291,17 +1291,85 @@ enum {
*/
};

static inline int f2fs_test_bit(unsigned int nr, char *addr);
static inline void f2fs_set_bit(unsigned int nr, char *addr);
static inline void f2fs_clear_bit(unsigned int nr, char *addr);

/*
* 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.
* Layout of f2fs page.private:
*
* Layout A: lowest bit should be 1
* | bit0 = 1 | bit1 | bit2 | ... | bit MAX | private data .... |
* bit 0 PAGE_PRIVATE_NOT_POINTER
* bit 1 PAGE_PRIVATE_ATOMIC_WRITE
* bit 2 PAGE_PRIVATE_DUMMY_WRITE
* bit 3 PAGE_PRIVATE_ONGOING_MIGRATION
* bit 4 PAGE_PRIVATE_INLINE_INODE
* bit 5 PAGE_PRIVATE_REF_RESOURCE
* bit 6- f2fs private data
*
* Layout B: lowest bit should be 0
* page.private is a wrapped pointer.
*/
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
#define DUMMY_WRITTEN_PAGE ((unsigned long)-2)
enum {
PAGE_PRIVATE_NOT_POINTER, /* private contains non-pointer data */
PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
PAGE_PRIVATE_DUMMY_WRITE, /* data page for padding aligned IO */
PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
PAGE_PRIVATE_MAX
};

#define PAGE_PRIVATE_GET_FUNC(name, flagname) \
static inline bool page_private_##name(struct page *page) \
{ \
return test_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)) && \
test_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}

#define PAGE_PRIVATE_SET_FUNC(name, flagname) \
static inline void set_page_private_##name(struct page *page) \
{ \
if (!PagePrivate(page)) { \
get_page(page); \
SetPagePrivate(page); \
} \
set_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)); \
set_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}

#define IS_ATOMIC_WRITTEN_PAGE(page) \
(page_private(page) == ATOMIC_WRITTEN_PAGE)
#define IS_DUMMY_WRITTEN_PAGE(page) \
(page_private(page) == DUMMY_WRITTEN_PAGE)
#define PAGE_PRIVATE_CLEAR_FUNC(name, flagname) \
static inline void clear_page_private_##name(struct page *page) \
{ \
clear_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
if (page_private(page) == 1 << PAGE_PRIVATE_NOT_POINTER) { \
set_page_private(page, 0); \
if (PagePrivate(page)) { \
ClearPagePrivate(page); \
put_page(page); \
}\
} \
}

PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
PAGE_PRIVATE_GET_FUNC(reference, REF_RESOURCE);
PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
PAGE_PRIVATE_GET_FUNC(dummy, DUMMY_WRITE);

PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
PAGE_PRIVATE_SET_FUNC(dummy, DUMMY_WRITE);

PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
PAGE_PRIVATE_CLEAR_FUNC(dummy, DUMMY_WRITE);

/* For compression */
enum compress_algorithm_type {
Expand Down Expand Up @@ -3169,20 +3237,6 @@ static inline bool __is_valid_data_blkaddr(block_t blkaddr)
return true;
}

static inline void f2fs_set_page_private(struct page *page,
unsigned long data)
{
if (PagePrivate(page))
return;

attach_page_private(page, (void *)data);
}

static inline void f2fs_clear_page_private(struct page *page)
{
detach_page_private(page);
}

/*
* file.c
*/
Expand Down
6 changes: 3 additions & 3 deletions fs/f2fs/gc.c
Expand Up @@ -1336,7 +1336,7 @@ static int move_data_page(struct inode *inode, block_t bidx, int gc_type,
goto out;
}
set_page_dirty(page);
set_cold_data(page);
set_page_private_gcing(page);
} else {
struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(inode),
Expand All @@ -1362,11 +1362,11 @@ static int move_data_page(struct inode *inode, block_t bidx, int gc_type,
f2fs_remove_dirty_inode(inode);
}

set_cold_data(page);
set_page_private_gcing(page);

err = f2fs_do_write_data_page(&fio);
if (err) {
clear_cold_data(page);
clear_page_private_gcing(page);
if (err == -ENOMEM) {
congestion_wait(BLK_RW_ASYNC,
DEFAULT_IO_TIMEOUT);
Expand Down
4 changes: 2 additions & 2 deletions fs/f2fs/inline.c
Expand Up @@ -173,7 +173,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)

/* clear inline data and flag after data writeback */
f2fs_truncate_inline_inode(dn->inode, dn->inode_page, 0);
clear_inline_node(dn->inode_page);
clear_page_private_inline(dn->inode_page);
clear_out:
stat_dec_inline_inode(dn->inode);
clear_inode_flag(dn->inode, FI_INLINE_DATA);
Expand Down Expand Up @@ -255,7 +255,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
set_inode_flag(inode, FI_APPEND_WRITE);
set_inode_flag(inode, FI_DATA_EXIST);

clear_inline_node(dn.inode_page);
clear_page_private_inline(dn.inode_page);
f2fs_put_dnode(&dn);
return 0;
}
Expand Down

0 comments on commit 2040daf

Please sign in to comment.