Merge tag 'for-f2fs-3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs
Pull f2fs updates from Jaegeuk Kim: "This patch-set includes the following major enhancement patches: - support inline xattrs - add sysfs support to control GCs explicitly - add proc entry to show the current segment usage information - improve the GC/SSR performance The other bug fixes are as follows: - avoid the overflow on status calculation - fix some error handling routines - fix inconsistent xattr states after power-off-recovery - fix incorrect xattr node offset definition - fix deadlock condition in fsync - fix the fdatasync routine for power-off-recovery" * tag 'for-f2fs-3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (40 commits) f2fs: optimize gc for better performance f2fs: merge more bios of node block writes f2fs: avoid an overflow during utilization calculation f2fs: trigger GC when there are prefree segments f2fs: use strncasecmp() simplify the string comparison f2fs: fix omitting to update inode page f2fs: support the inline xattrs f2fs: add the truncate_xattr_node function f2fs: introduce __find_xattr for readability f2fs: reserve the xattr space dynamically f2fs: add flags for inline xattrs f2fs: fix error return code in init_f2fs_fs() f2fs: fix wrong BUG_ON condition f2fs: fix memory leak when init f2fs filesystem fail f2fs: fix a compound statement label error f2fs: avoid writing inode redundantly when creating a file f2fs: alloc_page() doesn't return an ERR_PTR f2fs: should cover i_xattr_nid with its xattr node page lock f2fs: check the free space first in new_node_page f2fs: clean up the needless end 'return' of void function ...
This commit is contained in:
26
Documentation/ABI/testing/sysfs-fs-f2fs
Normal file
26
Documentation/ABI/testing/sysfs-fs-f2fs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
What: /sys/fs/f2fs/<disk>/gc_max_sleep_time
|
||||||
|
Date: July 2013
|
||||||
|
Contact: "Namjae Jeon" <namjae.jeon@samsung.com>
|
||||||
|
Description:
|
||||||
|
Controls the maximun sleep time for gc_thread. Time
|
||||||
|
is in milliseconds.
|
||||||
|
|
||||||
|
What: /sys/fs/f2fs/<disk>/gc_min_sleep_time
|
||||||
|
Date: July 2013
|
||||||
|
Contact: "Namjae Jeon" <namjae.jeon@samsung.com>
|
||||||
|
Description:
|
||||||
|
Controls the minimum sleep time for gc_thread. Time
|
||||||
|
is in milliseconds.
|
||||||
|
|
||||||
|
What: /sys/fs/f2fs/<disk>/gc_no_gc_sleep_time
|
||||||
|
Date: July 2013
|
||||||
|
Contact: "Namjae Jeon" <namjae.jeon@samsung.com>
|
||||||
|
Description:
|
||||||
|
Controls the default sleep time for gc_thread. Time
|
||||||
|
is in milliseconds.
|
||||||
|
|
||||||
|
What: /sys/fs/f2fs/<disk>/gc_idle
|
||||||
|
Date: July 2013
|
||||||
|
Contact: "Namjae Jeon" <namjae.jeon@samsung.com>
|
||||||
|
Description:
|
||||||
|
Controls the victim selection policy for garbage collection.
|
@@ -18,8 +18,8 @@ according to its internal geometry or flash memory management scheme, namely FTL
|
|||||||
F2FS and its tools support various parameters not only for configuring on-disk
|
F2FS and its tools support various parameters not only for configuring on-disk
|
||||||
layout, but also for selecting allocation and cleaning algorithms.
|
layout, but also for selecting allocation and cleaning algorithms.
|
||||||
|
|
||||||
The file system formatting tool, "mkfs.f2fs", is available from the following
|
The following git tree provides the file system formatting tool (mkfs.f2fs),
|
||||||
git tree:
|
a consistency checking tool (fsck.f2fs), and a debugging tool (dump.f2fs).
|
||||||
>> git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git
|
>> git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git
|
||||||
|
|
||||||
For reporting bugs and sending patches, please use the following mailing list:
|
For reporting bugs and sending patches, please use the following mailing list:
|
||||||
@@ -132,6 +132,38 @@ f2fs. Each file shows the whole f2fs information.
|
|||||||
- average SIT information about whole segments
|
- average SIT information about whole segments
|
||||||
- current memory footprint consumed by f2fs.
|
- current memory footprint consumed by f2fs.
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
SYSFS ENTRIES
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
Information about mounted f2f2 file systems can be found in
|
||||||
|
/sys/fs/f2fs. Each mounted filesystem will have a directory in
|
||||||
|
/sys/fs/f2fs based on its device name (i.e., /sys/fs/f2fs/sda).
|
||||||
|
The files in each per-device directory are shown in table below.
|
||||||
|
|
||||||
|
Files in /sys/fs/f2fs/<devname>
|
||||||
|
(see also Documentation/ABI/testing/sysfs-fs-f2fs)
|
||||||
|
..............................................................................
|
||||||
|
File Content
|
||||||
|
|
||||||
|
gc_max_sleep_time This tuning parameter controls the maximum sleep
|
||||||
|
time for the garbage collection thread. Time is
|
||||||
|
in milliseconds.
|
||||||
|
|
||||||
|
gc_min_sleep_time This tuning parameter controls the minimum sleep
|
||||||
|
time for the garbage collection thread. Time is
|
||||||
|
in milliseconds.
|
||||||
|
|
||||||
|
gc_no_gc_sleep_time This tuning parameter controls the default sleep
|
||||||
|
time for the garbage collection thread. Time is
|
||||||
|
in milliseconds.
|
||||||
|
|
||||||
|
gc_idle This parameter controls the selection of victim
|
||||||
|
policy for garbage collection. Setting gc_idle = 0
|
||||||
|
(default) will disable this option. Setting
|
||||||
|
gc_idle = 1 will select the Cost Benefit approach
|
||||||
|
& setting gc_idle = 2 will select the greedy aproach.
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
USAGE
|
USAGE
|
||||||
================================================================================
|
================================================================================
|
||||||
@@ -149,8 +181,12 @@ USAGE
|
|||||||
# mkfs.f2fs -l label /dev/block_device
|
# mkfs.f2fs -l label /dev/block_device
|
||||||
# mount -t f2fs /dev/block_device /mnt/f2fs
|
# mount -t f2fs /dev/block_device /mnt/f2fs
|
||||||
|
|
||||||
Format options
|
mkfs.f2fs
|
||||||
--------------
|
---------
|
||||||
|
The mkfs.f2fs is for the use of formatting a partition as the f2fs filesystem,
|
||||||
|
which builds a basic on-disk layout.
|
||||||
|
|
||||||
|
The options consist of:
|
||||||
-l [label] : Give a volume label, up to 512 unicode name.
|
-l [label] : Give a volume label, up to 512 unicode name.
|
||||||
-a [0 or 1] : Split start location of each area for heap-based allocation.
|
-a [0 or 1] : Split start location of each area for heap-based allocation.
|
||||||
1 is set by default, which performs this.
|
1 is set by default, which performs this.
|
||||||
@@ -164,6 +200,37 @@ Format options
|
|||||||
-t [0 or 1] : Disable discard command or not.
|
-t [0 or 1] : Disable discard command or not.
|
||||||
1 is set by default, which conducts discard.
|
1 is set by default, which conducts discard.
|
||||||
|
|
||||||
|
fsck.f2fs
|
||||||
|
---------
|
||||||
|
The fsck.f2fs is a tool to check the consistency of an f2fs-formatted
|
||||||
|
partition, which examines whether the filesystem metadata and user-made data
|
||||||
|
are cross-referenced correctly or not.
|
||||||
|
Note that, initial version of the tool does not fix any inconsistency.
|
||||||
|
|
||||||
|
The options consist of:
|
||||||
|
-d debug level [default:0]
|
||||||
|
|
||||||
|
dump.f2fs
|
||||||
|
---------
|
||||||
|
The dump.f2fs shows the information of specific inode and dumps SSA and SIT to
|
||||||
|
file. Each file is dump_ssa and dump_sit.
|
||||||
|
|
||||||
|
The dump.f2fs is used to debug on-disk data structures of the f2fs filesystem.
|
||||||
|
It shows on-disk inode information reconized by a given inode number, and is
|
||||||
|
able to dump all the SSA and SIT entries into predefined files, ./dump_ssa and
|
||||||
|
./dump_sit respectively.
|
||||||
|
|
||||||
|
The options consist of:
|
||||||
|
-d debug level [default:0]
|
||||||
|
-i inode no (hex)
|
||||||
|
-s [SIT dump segno from #1~#2 (decimal), for all 0~-1]
|
||||||
|
-a [SSA dump segno from #1~#2 (decimal), for all 0~-1]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# dump.f2fs -i [ino] /dev/sdx
|
||||||
|
# dump.f2fs -s 0~-1 /dev/sdx (SIT dump)
|
||||||
|
# dump.f2fs -a 0~-1 /dev/sdx (SSA dump)
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
DESIGN
|
DESIGN
|
||||||
================================================================================
|
================================================================================
|
||||||
|
@@ -182,7 +182,7 @@ const struct address_space_operations f2fs_meta_aops = {
|
|||||||
.set_page_dirty = f2fs_set_meta_page_dirty,
|
.set_page_dirty = f2fs_set_meta_page_dirty,
|
||||||
};
|
};
|
||||||
|
|
||||||
int check_orphan_space(struct f2fs_sb_info *sbi)
|
int acquire_orphan_inode(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
unsigned int max_orphans;
|
unsigned int max_orphans;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@@ -197,10 +197,19 @@ int check_orphan_space(struct f2fs_sb_info *sbi)
|
|||||||
mutex_lock(&sbi->orphan_inode_mutex);
|
mutex_lock(&sbi->orphan_inode_mutex);
|
||||||
if (sbi->n_orphans >= max_orphans)
|
if (sbi->n_orphans >= max_orphans)
|
||||||
err = -ENOSPC;
|
err = -ENOSPC;
|
||||||
|
else
|
||||||
|
sbi->n_orphans++;
|
||||||
mutex_unlock(&sbi->orphan_inode_mutex);
|
mutex_unlock(&sbi->orphan_inode_mutex);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void release_orphan_inode(struct f2fs_sb_info *sbi)
|
||||||
|
{
|
||||||
|
mutex_lock(&sbi->orphan_inode_mutex);
|
||||||
|
sbi->n_orphans--;
|
||||||
|
mutex_unlock(&sbi->orphan_inode_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
||||||
{
|
{
|
||||||
struct list_head *head, *this;
|
struct list_head *head, *this;
|
||||||
@@ -229,21 +238,18 @@ retry:
|
|||||||
list_add(&new->list, this->prev);
|
list_add(&new->list, this->prev);
|
||||||
else
|
else
|
||||||
list_add_tail(&new->list, head);
|
list_add_tail(&new->list, head);
|
||||||
|
|
||||||
sbi->n_orphans++;
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&sbi->orphan_inode_mutex);
|
mutex_unlock(&sbi->orphan_inode_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
||||||
{
|
{
|
||||||
struct list_head *this, *next, *head;
|
struct list_head *head;
|
||||||
struct orphan_inode_entry *orphan;
|
struct orphan_inode_entry *orphan;
|
||||||
|
|
||||||
mutex_lock(&sbi->orphan_inode_mutex);
|
mutex_lock(&sbi->orphan_inode_mutex);
|
||||||
head = &sbi->orphan_inode_list;
|
head = &sbi->orphan_inode_list;
|
||||||
list_for_each_safe(this, next, head) {
|
list_for_each_entry(orphan, head, list) {
|
||||||
orphan = list_entry(this, struct orphan_inode_entry, list);
|
|
||||||
if (orphan->ino == ino) {
|
if (orphan->ino == ino) {
|
||||||
list_del(&orphan->list);
|
list_del(&orphan->list);
|
||||||
kmem_cache_free(orphan_entry_slab, orphan);
|
kmem_cache_free(orphan_entry_slab, orphan);
|
||||||
@@ -373,7 +379,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
|||||||
if (!f2fs_crc_valid(crc, cp_block, crc_offset))
|
if (!f2fs_crc_valid(crc, cp_block, crc_offset))
|
||||||
goto invalid_cp1;
|
goto invalid_cp1;
|
||||||
|
|
||||||
pre_version = le64_to_cpu(cp_block->checkpoint_ver);
|
pre_version = cur_cp_version(cp_block);
|
||||||
|
|
||||||
/* Read the 2nd cp block in this CP pack */
|
/* Read the 2nd cp block in this CP pack */
|
||||||
cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1;
|
cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1;
|
||||||
@@ -388,7 +394,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
|||||||
if (!f2fs_crc_valid(crc, cp_block, crc_offset))
|
if (!f2fs_crc_valid(crc, cp_block, crc_offset))
|
||||||
goto invalid_cp2;
|
goto invalid_cp2;
|
||||||
|
|
||||||
cur_version = le64_to_cpu(cp_block->checkpoint_ver);
|
cur_version = cur_cp_version(cp_block);
|
||||||
|
|
||||||
if (cur_version == pre_version) {
|
if (cur_version == pre_version) {
|
||||||
*version = cur_version;
|
*version = cur_version;
|
||||||
@@ -793,7 +799,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
|
|||||||
* Increase the version number so that
|
* Increase the version number so that
|
||||||
* SIT entries and seg summaries are written at correct place
|
* SIT entries and seg summaries are written at correct place
|
||||||
*/
|
*/
|
||||||
ckpt_ver = le64_to_cpu(ckpt->checkpoint_ver);
|
ckpt_ver = cur_cp_version(ckpt);
|
||||||
ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver);
|
ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver);
|
||||||
|
|
||||||
/* write cached NAT/SIT entries to NAT/SIT area */
|
/* write cached NAT/SIT entries to NAT/SIT area */
|
||||||
|
@@ -37,9 +37,9 @@ static void __set_data_blkaddr(struct dnode_of_data *dn, block_t new_addr)
|
|||||||
struct page *node_page = dn->node_page;
|
struct page *node_page = dn->node_page;
|
||||||
unsigned int ofs_in_node = dn->ofs_in_node;
|
unsigned int ofs_in_node = dn->ofs_in_node;
|
||||||
|
|
||||||
wait_on_page_writeback(node_page);
|
f2fs_wait_on_page_writeback(node_page, NODE, false);
|
||||||
|
|
||||||
rn = (struct f2fs_node *)page_address(node_page);
|
rn = F2FS_NODE(node_page);
|
||||||
|
|
||||||
/* Get physical address of data block */
|
/* Get physical address of data block */
|
||||||
addr_array = blkaddr_in_node(rn);
|
addr_array = blkaddr_in_node(rn);
|
||||||
@@ -117,7 +117,8 @@ void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn)
|
|||||||
block_t start_blkaddr, end_blkaddr;
|
block_t start_blkaddr, end_blkaddr;
|
||||||
|
|
||||||
BUG_ON(blk_addr == NEW_ADDR);
|
BUG_ON(blk_addr == NEW_ADDR);
|
||||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page)) + dn->ofs_in_node;
|
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
|
||||||
|
dn->ofs_in_node;
|
||||||
|
|
||||||
/* Update the page address in the parent node */
|
/* Update the page address in the parent node */
|
||||||
__set_data_blkaddr(dn, blk_addr);
|
__set_data_blkaddr(dn, blk_addr);
|
||||||
@@ -176,7 +177,6 @@ void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn)
|
|||||||
end_update:
|
end_update:
|
||||||
write_unlock(&fi->ext.ext_lock);
|
write_unlock(&fi->ext.ext_lock);
|
||||||
sync_inode_page(dn);
|
sync_inode_page(dn);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
|
struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
|
||||||
@@ -260,8 +260,17 @@ repeat:
|
|||||||
if (PageUptodate(page))
|
if (PageUptodate(page))
|
||||||
return page;
|
return page;
|
||||||
|
|
||||||
BUG_ON(dn.data_blkaddr == NEW_ADDR);
|
/*
|
||||||
BUG_ON(dn.data_blkaddr == NULL_ADDR);
|
* A new dentry page is allocated but not able to be written, since its
|
||||||
|
* new inode page couldn't be allocated due to -ENOSPC.
|
||||||
|
* In such the case, its blkaddr can be remained as NEW_ADDR.
|
||||||
|
* see, f2fs_add_link -> get_new_data_page -> init_inode_metadata.
|
||||||
|
*/
|
||||||
|
if (dn.data_blkaddr == NEW_ADDR) {
|
||||||
|
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||||
|
SetPageUptodate(page);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
|
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -365,7 +374,6 @@ static void read_end_io(struct bio *bio, int err)
|
|||||||
}
|
}
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
} while (bvec >= bio->bi_io_vec);
|
} while (bvec >= bio->bi_io_vec);
|
||||||
kfree(bio->bi_private);
|
|
||||||
bio_put(bio);
|
bio_put(bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +399,6 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
|
|||||||
bio->bi_end_io = read_end_io;
|
bio->bi_end_io = read_end_io;
|
||||||
|
|
||||||
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
|
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
|
||||||
kfree(bio->bi_private);
|
|
||||||
bio_put(bio);
|
bio_put(bio);
|
||||||
up_read(&sbi->bio_sem);
|
up_read(&sbi->bio_sem);
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
@@ -442,7 +449,7 @@ static int get_data_block_ro(struct inode *inode, sector_t iblock,
|
|||||||
unsigned int end_offset;
|
unsigned int end_offset;
|
||||||
|
|
||||||
end_offset = IS_INODE(dn.node_page) ?
|
end_offset = IS_INODE(dn.node_page) ?
|
||||||
ADDRS_PER_INODE :
|
ADDRS_PER_INODE(F2FS_I(inode)) :
|
||||||
ADDRS_PER_BLOCK;
|
ADDRS_PER_BLOCK;
|
||||||
|
|
||||||
clear_buffer_new(bh_result);
|
clear_buffer_new(bh_result);
|
||||||
@@ -636,9 +643,6 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
int ilock;
|
int ilock;
|
||||||
|
|
||||||
/* for nobh_write_end */
|
|
||||||
*fsdata = NULL;
|
|
||||||
|
|
||||||
f2fs_balance_fs(sbi);
|
f2fs_balance_fs(sbi);
|
||||||
repeat:
|
repeat:
|
||||||
page = grab_cache_page_write_begin(mapping, index, flags);
|
page = grab_cache_page_write_begin(mapping, index, flags);
|
||||||
|
@@ -29,7 +29,7 @@ static DEFINE_MUTEX(f2fs_stat_mutex);
|
|||||||
|
|
||||||
static void update_general_status(struct f2fs_sb_info *sbi)
|
static void update_general_status(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_stat_info *si = sbi->stat_info;
|
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* valid check of the segment numbers */
|
/* valid check of the segment numbers */
|
||||||
@@ -83,7 +83,7 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||||||
*/
|
*/
|
||||||
static void update_sit_info(struct f2fs_sb_info *sbi)
|
static void update_sit_info(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_stat_info *si = sbi->stat_info;
|
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||||
unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
|
unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
|
||||||
struct sit_info *sit_i = SIT_I(sbi);
|
struct sit_info *sit_i = SIT_I(sbi);
|
||||||
unsigned int segno, vblocks;
|
unsigned int segno, vblocks;
|
||||||
@@ -118,7 +118,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
|
|||||||
*/
|
*/
|
||||||
static void update_mem_info(struct f2fs_sb_info *sbi)
|
static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_stat_info *si = sbi->stat_info;
|
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||||
unsigned npages;
|
unsigned npages;
|
||||||
|
|
||||||
if (si->base_mem)
|
if (si->base_mem)
|
||||||
@@ -253,21 +253,21 @@ static int stat_show(struct seq_file *s, void *v)
|
|||||||
si->nats, NM_WOUT_THRESHOLD);
|
si->nats, NM_WOUT_THRESHOLD);
|
||||||
seq_printf(s, " - SITs: %5d\n - free_nids: %5d\n",
|
seq_printf(s, " - SITs: %5d\n - free_nids: %5d\n",
|
||||||
si->sits, si->fnids);
|
si->sits, si->fnids);
|
||||||
seq_printf(s, "\nDistribution of User Blocks:");
|
seq_puts(s, "\nDistribution of User Blocks:");
|
||||||
seq_printf(s, " [ valid | invalid | free ]\n");
|
seq_puts(s, " [ valid | invalid | free ]\n");
|
||||||
seq_printf(s, " [");
|
seq_puts(s, " [");
|
||||||
|
|
||||||
for (j = 0; j < si->util_valid; j++)
|
for (j = 0; j < si->util_valid; j++)
|
||||||
seq_printf(s, "-");
|
seq_putc(s, '-');
|
||||||
seq_printf(s, "|");
|
seq_putc(s, '|');
|
||||||
|
|
||||||
for (j = 0; j < si->util_invalid; j++)
|
for (j = 0; j < si->util_invalid; j++)
|
||||||
seq_printf(s, "-");
|
seq_putc(s, '-');
|
||||||
seq_printf(s, "|");
|
seq_putc(s, '|');
|
||||||
|
|
||||||
for (j = 0; j < si->util_free; j++)
|
for (j = 0; j < si->util_free; j++)
|
||||||
seq_printf(s, "-");
|
seq_putc(s, '-');
|
||||||
seq_printf(s, "]\n\n");
|
seq_puts(s, "]\n\n");
|
||||||
seq_printf(s, "SSR: %u blocks in %u segments\n",
|
seq_printf(s, "SSR: %u blocks in %u segments\n",
|
||||||
si->block_count[SSR], si->segment_count[SSR]);
|
si->block_count[SSR], si->segment_count[SSR]);
|
||||||
seq_printf(s, "LFS: %u blocks in %u segments\n",
|
seq_printf(s, "LFS: %u blocks in %u segments\n",
|
||||||
@@ -305,11 +305,10 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
|||||||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
||||||
struct f2fs_stat_info *si;
|
struct f2fs_stat_info *si;
|
||||||
|
|
||||||
sbi->stat_info = kzalloc(sizeof(struct f2fs_stat_info), GFP_KERNEL);
|
si = kzalloc(sizeof(struct f2fs_stat_info), GFP_KERNEL);
|
||||||
if (!sbi->stat_info)
|
if (!si)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
si = sbi->stat_info;
|
|
||||||
si->all_area_segs = le32_to_cpu(raw_super->segment_count);
|
si->all_area_segs = le32_to_cpu(raw_super->segment_count);
|
||||||
si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit);
|
si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit);
|
||||||
si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat);
|
si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat);
|
||||||
@@ -319,6 +318,7 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
|||||||
si->main_area_zones = si->main_area_sections /
|
si->main_area_zones = si->main_area_sections /
|
||||||
le32_to_cpu(raw_super->secs_per_zone);
|
le32_to_cpu(raw_super->secs_per_zone);
|
||||||
si->sbi = sbi;
|
si->sbi = sbi;
|
||||||
|
sbi->stat_info = si;
|
||||||
|
|
||||||
mutex_lock(&f2fs_stat_mutex);
|
mutex_lock(&f2fs_stat_mutex);
|
||||||
list_add_tail(&si->stat_list, &f2fs_stat_list);
|
list_add_tail(&si->stat_list, &f2fs_stat_list);
|
||||||
@@ -329,13 +329,13 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
|||||||
|
|
||||||
void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
|
void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_stat_info *si = sbi->stat_info;
|
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||||
|
|
||||||
mutex_lock(&f2fs_stat_mutex);
|
mutex_lock(&f2fs_stat_mutex);
|
||||||
list_del(&si->stat_list);
|
list_del(&si->stat_list);
|
||||||
mutex_unlock(&f2fs_stat_mutex);
|
mutex_unlock(&f2fs_stat_mutex);
|
||||||
|
|
||||||
kfree(sbi->stat_info);
|
kfree(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __init f2fs_create_root_stats(void)
|
void __init f2fs_create_root_stats(void)
|
||||||
|
@@ -270,12 +270,27 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
|
|||||||
struct f2fs_node *rn;
|
struct f2fs_node *rn;
|
||||||
|
|
||||||
/* copy name info. to this inode page */
|
/* copy name info. to this inode page */
|
||||||
rn = (struct f2fs_node *)page_address(ipage);
|
rn = F2FS_NODE(ipage);
|
||||||
rn->i.i_namelen = cpu_to_le32(name->len);
|
rn->i.i_namelen = cpu_to_le32(name->len);
|
||||||
memcpy(rn->i.i_name, name->name, name->len);
|
memcpy(rn->i.i_name, name->name, name->len);
|
||||||
set_page_dirty(ipage);
|
set_page_dirty(ipage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int update_dent_inode(struct inode *inode, const struct qstr *name)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
|
struct page *page;
|
||||||
|
|
||||||
|
page = get_node_page(sbi, inode->i_ino);
|
||||||
|
if (IS_ERR(page))
|
||||||
|
return PTR_ERR(page);
|
||||||
|
|
||||||
|
init_dent_inode(name, page);
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int make_empty_dir(struct inode *inode,
|
static int make_empty_dir(struct inode *inode,
|
||||||
struct inode *parent, struct page *page)
|
struct inode *parent, struct page *page)
|
||||||
{
|
{
|
||||||
@@ -557,6 +572,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
|||||||
|
|
||||||
if (inode->i_nlink == 0)
|
if (inode->i_nlink == 0)
|
||||||
add_orphan_inode(sbi, inode->i_ino);
|
add_orphan_inode(sbi, inode->i_ino);
|
||||||
|
else
|
||||||
|
release_orphan_inode(sbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bit_pos == NR_DENTRY_IN_BLOCK) {
|
if (bit_pos == NR_DENTRY_IN_BLOCK) {
|
||||||
|
106
fs/f2fs/f2fs.h
106
fs/f2fs/f2fs.h
@@ -17,6 +17,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
#include <linux/kobject.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For mount options
|
* For mount options
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#define F2FS_MOUNT_XATTR_USER 0x00000010
|
#define F2FS_MOUNT_XATTR_USER 0x00000010
|
||||||
#define F2FS_MOUNT_POSIX_ACL 0x00000020
|
#define F2FS_MOUNT_POSIX_ACL 0x00000020
|
||||||
#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040
|
#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040
|
||||||
|
#define F2FS_MOUNT_INLINE_XATTR 0x00000080
|
||||||
|
|
||||||
#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
|
#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
|
||||||
#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
|
#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
|
||||||
@@ -134,11 +136,13 @@ static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i)
|
|||||||
/*
|
/*
|
||||||
* For INODE and NODE manager
|
* For INODE and NODE manager
|
||||||
*/
|
*/
|
||||||
#define XATTR_NODE_OFFSET (-1) /*
|
/*
|
||||||
* store xattrs to one node block per
|
* XATTR_NODE_OFFSET stores xattrs to one node block per file keeping -1
|
||||||
* file keeping -1 as its node offset to
|
* as its node offset to distinguish from index node blocks.
|
||||||
* distinguish from index node blocks.
|
* But some bits are used to mark the node block.
|
||||||
*/
|
*/
|
||||||
|
#define XATTR_NODE_OFFSET ((((unsigned int)-1) << OFFSET_BIT_SHIFT) \
|
||||||
|
>> OFFSET_BIT_SHIFT)
|
||||||
enum {
|
enum {
|
||||||
ALLOC_NODE, /* allocate a new node page if needed */
|
ALLOC_NODE, /* allocate a new node page if needed */
|
||||||
LOOKUP_NODE, /* look up a node without readahead */
|
LOOKUP_NODE, /* look up a node without readahead */
|
||||||
@@ -178,6 +182,7 @@ struct f2fs_inode_info {
|
|||||||
f2fs_hash_t chash; /* hash value of given file name */
|
f2fs_hash_t chash; /* hash value of given file name */
|
||||||
unsigned int clevel; /* maximum level of given file name */
|
unsigned int clevel; /* maximum level of given file name */
|
||||||
nid_t i_xattr_nid; /* node id that contains xattrs */
|
nid_t i_xattr_nid; /* node id that contains xattrs */
|
||||||
|
unsigned long long xattr_ver; /* cp version of xattr modification */
|
||||||
struct extent_info ext; /* in-memory extent cache entry */
|
struct extent_info ext; /* in-memory extent cache entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -295,15 +300,6 @@ struct f2fs_sm_info {
|
|||||||
unsigned int ovp_segments; /* # of overprovision segments */
|
unsigned int ovp_segments; /* # of overprovision segments */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* For directory operation
|
|
||||||
*/
|
|
||||||
#define NODE_DIR1_BLOCK (ADDRS_PER_INODE + 1)
|
|
||||||
#define NODE_DIR2_BLOCK (ADDRS_PER_INODE + 2)
|
|
||||||
#define NODE_IND1_BLOCK (ADDRS_PER_INODE + 3)
|
|
||||||
#define NODE_IND2_BLOCK (ADDRS_PER_INODE + 4)
|
|
||||||
#define NODE_DIND_BLOCK (ADDRS_PER_INODE + 5)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For superblock
|
* For superblock
|
||||||
*/
|
*/
|
||||||
@@ -350,6 +346,7 @@ enum page_type {
|
|||||||
|
|
||||||
struct f2fs_sb_info {
|
struct f2fs_sb_info {
|
||||||
struct super_block *sb; /* pointer to VFS super block */
|
struct super_block *sb; /* pointer to VFS super block */
|
||||||
|
struct proc_dir_entry *s_proc; /* proc entry */
|
||||||
struct buffer_head *raw_super_buf; /* buffer head of raw sb */
|
struct buffer_head *raw_super_buf; /* buffer head of raw sb */
|
||||||
struct f2fs_super_block *raw_super; /* raw super block pointer */
|
struct f2fs_super_block *raw_super; /* raw super block pointer */
|
||||||
int s_dirty; /* dirty flag for checkpoint */
|
int s_dirty; /* dirty flag for checkpoint */
|
||||||
@@ -429,6 +426,10 @@ struct f2fs_sb_info {
|
|||||||
#endif
|
#endif
|
||||||
unsigned int last_victim[2]; /* last victim segment # */
|
unsigned int last_victim[2]; /* last victim segment # */
|
||||||
spinlock_t stat_lock; /* lock for stat operations */
|
spinlock_t stat_lock; /* lock for stat operations */
|
||||||
|
|
||||||
|
/* For sysfs suppport */
|
||||||
|
struct kobject s_kobj;
|
||||||
|
struct completion s_kobj_unregister;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -454,6 +455,11 @@ static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi)
|
|||||||
return (struct f2fs_checkpoint *)(sbi->ckpt);
|
return (struct f2fs_checkpoint *)(sbi->ckpt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct f2fs_node *F2FS_NODE(struct page *page)
|
||||||
|
{
|
||||||
|
return (struct f2fs_node *)page_address(page);
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi)
|
static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
return (struct f2fs_nm_info *)(sbi->nm_info);
|
return (struct f2fs_nm_info *)(sbi->nm_info);
|
||||||
@@ -489,6 +495,11 @@ static inline void F2FS_RESET_SB_DIRT(struct f2fs_sb_info *sbi)
|
|||||||
sbi->s_dirty = 0;
|
sbi->s_dirty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp)
|
||||||
|
{
|
||||||
|
return le64_to_cpu(cp->checkpoint_ver);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
|
static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
|
||||||
{
|
{
|
||||||
unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags);
|
unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags);
|
||||||
@@ -677,7 +688,7 @@ static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi)
|
|||||||
{
|
{
|
||||||
block_t start_addr;
|
block_t start_addr;
|
||||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||||
unsigned long long ckpt_version = le64_to_cpu(ckpt->checkpoint_ver);
|
unsigned long long ckpt_version = cur_cp_version(ckpt);
|
||||||
|
|
||||||
start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
|
start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
|
||||||
|
|
||||||
@@ -812,7 +823,7 @@ static inline struct kmem_cache *f2fs_kmem_cache_create(const char *name,
|
|||||||
|
|
||||||
static inline bool IS_INODE(struct page *page)
|
static inline bool IS_INODE(struct page *page)
|
||||||
{
|
{
|
||||||
struct f2fs_node *p = (struct f2fs_node *)page_address(page);
|
struct f2fs_node *p = F2FS_NODE(page);
|
||||||
return RAW_IS_INODE(p);
|
return RAW_IS_INODE(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,7 +837,7 @@ static inline block_t datablock_addr(struct page *node_page,
|
|||||||
{
|
{
|
||||||
struct f2fs_node *raw_node;
|
struct f2fs_node *raw_node;
|
||||||
__le32 *addr_array;
|
__le32 *addr_array;
|
||||||
raw_node = (struct f2fs_node *)page_address(node_page);
|
raw_node = F2FS_NODE(node_page);
|
||||||
addr_array = blkaddr_in_node(raw_node);
|
addr_array = blkaddr_in_node(raw_node);
|
||||||
return le32_to_cpu(addr_array[offset]);
|
return le32_to_cpu(addr_array[offset]);
|
||||||
}
|
}
|
||||||
@@ -873,6 +884,7 @@ enum {
|
|||||||
FI_NO_ALLOC, /* should not allocate any blocks */
|
FI_NO_ALLOC, /* should not allocate any blocks */
|
||||||
FI_UPDATE_DIR, /* should update inode block for consistency */
|
FI_UPDATE_DIR, /* should update inode block for consistency */
|
||||||
FI_DELAY_IPUT, /* used for the recovery */
|
FI_DELAY_IPUT, /* used for the recovery */
|
||||||
|
FI_INLINE_XATTR, /* used for inline xattr */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
|
static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
|
||||||
@@ -905,6 +917,45 @@ static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void get_inline_info(struct f2fs_inode_info *fi,
|
||||||
|
struct f2fs_inode *ri)
|
||||||
|
{
|
||||||
|
if (ri->i_inline & F2FS_INLINE_XATTR)
|
||||||
|
set_inode_flag(fi, FI_INLINE_XATTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_raw_inline(struct f2fs_inode_info *fi,
|
||||||
|
struct f2fs_inode *ri)
|
||||||
|
{
|
||||||
|
ri->i_inline = 0;
|
||||||
|
|
||||||
|
if (is_inode_flag_set(fi, FI_INLINE_XATTR))
|
||||||
|
ri->i_inline |= F2FS_INLINE_XATTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi)
|
||||||
|
{
|
||||||
|
if (is_inode_flag_set(fi, FI_INLINE_XATTR))
|
||||||
|
return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS;
|
||||||
|
return DEF_ADDRS_PER_INODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *inline_xattr_addr(struct page *page)
|
||||||
|
{
|
||||||
|
struct f2fs_inode *ri;
|
||||||
|
ri = (struct f2fs_inode *)page_address(page);
|
||||||
|
return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE -
|
||||||
|
F2FS_INLINE_XATTR_ADDRS]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int inline_xattr_size(struct inode *inode)
|
||||||
|
{
|
||||||
|
if (is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR))
|
||||||
|
return F2FS_INLINE_XATTR_ADDRS << 2;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int f2fs_readonly(struct super_block *sb)
|
static inline int f2fs_readonly(struct super_block *sb)
|
||||||
{
|
{
|
||||||
return sb->s_flags & MS_RDONLY;
|
return sb->s_flags & MS_RDONLY;
|
||||||
@@ -947,6 +998,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **);
|
|||||||
ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
|
ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
|
||||||
void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
|
void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
|
||||||
struct page *, struct inode *);
|
struct page *, struct inode *);
|
||||||
|
int update_dent_inode(struct inode *, const struct qstr *);
|
||||||
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
|
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
|
||||||
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *);
|
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *);
|
||||||
int f2fs_make_empty(struct inode *, struct inode *);
|
int f2fs_make_empty(struct inode *, struct inode *);
|
||||||
@@ -980,6 +1032,7 @@ int is_checkpointed_node(struct f2fs_sb_info *, nid_t);
|
|||||||
void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
|
void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
|
||||||
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
|
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
|
||||||
int truncate_inode_blocks(struct inode *, pgoff_t);
|
int truncate_inode_blocks(struct inode *, pgoff_t);
|
||||||
|
int truncate_xattr_node(struct inode *, struct page *);
|
||||||
int remove_inode_page(struct inode *);
|
int remove_inode_page(struct inode *);
|
||||||
struct page *new_inode_page(struct inode *, const struct qstr *);
|
struct page *new_inode_page(struct inode *, const struct qstr *);
|
||||||
struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *);
|
struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *);
|
||||||
@@ -1012,7 +1065,8 @@ int npages_for_summary_flush(struct f2fs_sb_info *);
|
|||||||
void allocate_new_segments(struct f2fs_sb_info *);
|
void allocate_new_segments(struct f2fs_sb_info *);
|
||||||
struct page *get_sum_page(struct f2fs_sb_info *, unsigned int);
|
struct page *get_sum_page(struct f2fs_sb_info *, unsigned int);
|
||||||
struct bio *f2fs_bio_alloc(struct block_device *, int);
|
struct bio *f2fs_bio_alloc(struct block_device *, int);
|
||||||
void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool sync);
|
void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool);
|
||||||
|
void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool);
|
||||||
void write_meta_page(struct f2fs_sb_info *, struct page *);
|
void write_meta_page(struct f2fs_sb_info *, struct page *);
|
||||||
void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int,
|
void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int,
|
||||||
block_t, block_t *);
|
block_t, block_t *);
|
||||||
@@ -1037,7 +1091,8 @@ void destroy_segment_manager(struct f2fs_sb_info *);
|
|||||||
struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
|
struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
|
||||||
struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
|
struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
|
||||||
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
|
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
|
||||||
int check_orphan_space(struct f2fs_sb_info *);
|
int acquire_orphan_inode(struct f2fs_sb_info *);
|
||||||
|
void release_orphan_inode(struct f2fs_sb_info *);
|
||||||
void add_orphan_inode(struct f2fs_sb_info *, nid_t);
|
void add_orphan_inode(struct f2fs_sb_info *, nid_t);
|
||||||
void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
|
void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
|
||||||
int recover_orphan_inodes(struct f2fs_sb_info *);
|
int recover_orphan_inodes(struct f2fs_sb_info *);
|
||||||
@@ -1068,7 +1123,7 @@ int do_write_data_page(struct page *);
|
|||||||
*/
|
*/
|
||||||
int start_gc_thread(struct f2fs_sb_info *);
|
int start_gc_thread(struct f2fs_sb_info *);
|
||||||
void stop_gc_thread(struct f2fs_sb_info *);
|
void stop_gc_thread(struct f2fs_sb_info *);
|
||||||
block_t start_bidx_of_node(unsigned int);
|
block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *);
|
||||||
int f2fs_gc(struct f2fs_sb_info *);
|
int f2fs_gc(struct f2fs_sb_info *);
|
||||||
void build_gc_manager(struct f2fs_sb_info *);
|
void build_gc_manager(struct f2fs_sb_info *);
|
||||||
int __init create_gc_caches(void);
|
int __init create_gc_caches(void);
|
||||||
@@ -1112,11 +1167,16 @@ struct f2fs_stat_info {
|
|||||||
unsigned base_mem, cache_mem;
|
unsigned base_mem, cache_mem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
|
||||||
|
{
|
||||||
|
return (struct f2fs_stat_info*)sbi->stat_info;
|
||||||
|
}
|
||||||
|
|
||||||
#define stat_inc_call_count(si) ((si)->call_count++)
|
#define stat_inc_call_count(si) ((si)->call_count++)
|
||||||
|
|
||||||
#define stat_inc_seg_count(sbi, type) \
|
#define stat_inc_seg_count(sbi, type) \
|
||||||
do { \
|
do { \
|
||||||
struct f2fs_stat_info *si = sbi->stat_info; \
|
struct f2fs_stat_info *si = F2FS_STAT(sbi); \
|
||||||
(si)->tot_segs++; \
|
(si)->tot_segs++; \
|
||||||
if (type == SUM_TYPE_DATA) \
|
if (type == SUM_TYPE_DATA) \
|
||||||
si->data_segs++; \
|
si->data_segs++; \
|
||||||
@@ -1129,14 +1189,14 @@ struct f2fs_stat_info {
|
|||||||
|
|
||||||
#define stat_inc_data_blk_count(sbi, blks) \
|
#define stat_inc_data_blk_count(sbi, blks) \
|
||||||
do { \
|
do { \
|
||||||
struct f2fs_stat_info *si = sbi->stat_info; \
|
struct f2fs_stat_info *si = F2FS_STAT(sbi); \
|
||||||
stat_inc_tot_blk_count(si, blks); \
|
stat_inc_tot_blk_count(si, blks); \
|
||||||
si->data_blks += (blks); \
|
si->data_blks += (blks); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define stat_inc_node_blk_count(sbi, blks) \
|
#define stat_inc_node_blk_count(sbi, blks) \
|
||||||
do { \
|
do { \
|
||||||
struct f2fs_stat_info *si = sbi->stat_info; \
|
struct f2fs_stat_info *si = F2FS_STAT(sbi); \
|
||||||
stat_inc_tot_blk_count(si, blks); \
|
stat_inc_tot_blk_count(si, blks); \
|
||||||
si->node_blks += (blks); \
|
si->node_blks += (blks); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@@ -112,11 +112,13 @@ static int get_parent_ino(struct inode *inode, nid_t *pino)
|
|||||||
if (!dentry)
|
if (!dentry)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
inode = igrab(dentry->d_parent->d_inode);
|
if (update_dent_inode(inode, &dentry->d_name)) {
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
*pino = inode->i_ino;
|
*pino = parent_ino(dentry);
|
||||||
iput(inode);
|
dput(dentry);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,9 +149,10 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
|
|
||||||
if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
|
/*
|
||||||
goto out;
|
* Both of fdatasync() and fsync() are able to be recovered from
|
||||||
|
* sudden-power-off.
|
||||||
|
*/
|
||||||
if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
|
if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
|
||||||
need_cp = true;
|
need_cp = true;
|
||||||
else if (file_wrong_pino(inode))
|
else if (file_wrong_pino(inode))
|
||||||
@@ -158,10 +161,14 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||||||
need_cp = true;
|
need_cp = true;
|
||||||
else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
|
else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
|
||||||
need_cp = true;
|
need_cp = true;
|
||||||
|
else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi)))
|
||||||
|
need_cp = true;
|
||||||
|
|
||||||
if (need_cp) {
|
if (need_cp) {
|
||||||
nid_t pino;
|
nid_t pino;
|
||||||
|
|
||||||
|
F2FS_I(inode)->xattr_ver = 0;
|
||||||
|
|
||||||
/* all the dirty node pages should be flushed for POR */
|
/* all the dirty node pages should be flushed for POR */
|
||||||
ret = f2fs_sync_fs(inode->i_sb, 1);
|
ret = f2fs_sync_fs(inode->i_sb, 1);
|
||||||
if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
|
if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
|
||||||
@@ -205,7 +212,7 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
|||||||
struct f2fs_node *raw_node;
|
struct f2fs_node *raw_node;
|
||||||
__le32 *addr;
|
__le32 *addr;
|
||||||
|
|
||||||
raw_node = page_address(dn->node_page);
|
raw_node = F2FS_NODE(dn->node_page);
|
||||||
addr = blkaddr_in_node(raw_node) + ofs;
|
addr = blkaddr_in_node(raw_node) + ofs;
|
||||||
|
|
||||||
for ( ; count > 0; count--, addr++, dn->ofs_in_node++) {
|
for ( ; count > 0; count--, addr++, dn->ofs_in_node++) {
|
||||||
@@ -283,7 +290,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (IS_INODE(dn.node_page))
|
if (IS_INODE(dn.node_page))
|
||||||
count = ADDRS_PER_INODE;
|
count = ADDRS_PER_INODE(F2FS_I(inode));
|
||||||
else
|
else
|
||||||
count = ADDRS_PER_BLOCK;
|
count = ADDRS_PER_BLOCK;
|
||||||
|
|
||||||
|
58
fs/f2fs/gc.c
58
fs/f2fs/gc.c
@@ -29,10 +29,11 @@ static struct kmem_cache *winode_slab;
|
|||||||
static int gc_thread_func(void *data)
|
static int gc_thread_func(void *data)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = data;
|
struct f2fs_sb_info *sbi = data;
|
||||||
|
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||||
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
||||||
long wait_ms;
|
long wait_ms;
|
||||||
|
|
||||||
wait_ms = GC_THREAD_MIN_SLEEP_TIME;
|
wait_ms = gc_th->min_sleep_time;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (try_to_freeze())
|
if (try_to_freeze())
|
||||||
@@ -45,7 +46,7 @@ static int gc_thread_func(void *data)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) {
|
if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) {
|
||||||
wait_ms = GC_THREAD_MAX_SLEEP_TIME;
|
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +67,15 @@ static int gc_thread_func(void *data)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!is_idle(sbi)) {
|
if (!is_idle(sbi)) {
|
||||||
wait_ms = increase_sleep_time(wait_ms);
|
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||||
mutex_unlock(&sbi->gc_mutex);
|
mutex_unlock(&sbi->gc_mutex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_enough_invalid_blocks(sbi))
|
if (has_enough_invalid_blocks(sbi))
|
||||||
wait_ms = decrease_sleep_time(wait_ms);
|
wait_ms = decrease_sleep_time(gc_th, wait_ms);
|
||||||
else
|
else
|
||||||
wait_ms = increase_sleep_time(wait_ms);
|
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_STAT_FS
|
#ifdef CONFIG_F2FS_STAT_FS
|
||||||
sbi->bg_gc++;
|
sbi->bg_gc++;
|
||||||
@@ -82,7 +83,7 @@ static int gc_thread_func(void *data)
|
|||||||
|
|
||||||
/* if return value is not zero, no victim was selected */
|
/* if return value is not zero, no victim was selected */
|
||||||
if (f2fs_gc(sbi))
|
if (f2fs_gc(sbi))
|
||||||
wait_ms = GC_THREAD_NOGC_SLEEP_TIME;
|
wait_ms = gc_th->no_gc_sleep_time;
|
||||||
} while (!kthread_should_stop());
|
} while (!kthread_should_stop());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -101,6 +102,12 @@ int start_gc_thread(struct f2fs_sb_info *sbi)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
|
||||||
|
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
|
||||||
|
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
|
||||||
|
|
||||||
|
gc_th->gc_idle = 0;
|
||||||
|
|
||||||
sbi->gc_thread = gc_th;
|
sbi->gc_thread = gc_th;
|
||||||
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
||||||
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
|
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
|
||||||
@@ -125,9 +132,17 @@ void stop_gc_thread(struct f2fs_sb_info *sbi)
|
|||||||
sbi->gc_thread = NULL;
|
sbi->gc_thread = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int select_gc_type(int gc_type)
|
static int select_gc_type(struct f2fs_gc_kthread *gc_th, int gc_type)
|
||||||
{
|
{
|
||||||
return (gc_type == BG_GC) ? GC_CB : GC_GREEDY;
|
int gc_mode = (gc_type == BG_GC) ? GC_CB : GC_GREEDY;
|
||||||
|
|
||||||
|
if (gc_th && gc_th->gc_idle) {
|
||||||
|
if (gc_th->gc_idle == 1)
|
||||||
|
gc_mode = GC_CB;
|
||||||
|
else if (gc_th->gc_idle == 2)
|
||||||
|
gc_mode = GC_GREEDY;
|
||||||
|
}
|
||||||
|
return gc_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
|
static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
|
||||||
@@ -138,12 +153,18 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
|
|||||||
if (p->alloc_mode == SSR) {
|
if (p->alloc_mode == SSR) {
|
||||||
p->gc_mode = GC_GREEDY;
|
p->gc_mode = GC_GREEDY;
|
||||||
p->dirty_segmap = dirty_i->dirty_segmap[type];
|
p->dirty_segmap = dirty_i->dirty_segmap[type];
|
||||||
|
p->max_search = dirty_i->nr_dirty[type];
|
||||||
p->ofs_unit = 1;
|
p->ofs_unit = 1;
|
||||||
} else {
|
} else {
|
||||||
p->gc_mode = select_gc_type(gc_type);
|
p->gc_mode = select_gc_type(sbi->gc_thread, gc_type);
|
||||||
p->dirty_segmap = dirty_i->dirty_segmap[DIRTY];
|
p->dirty_segmap = dirty_i->dirty_segmap[DIRTY];
|
||||||
|
p->max_search = dirty_i->nr_dirty[DIRTY];
|
||||||
p->ofs_unit = sbi->segs_per_sec;
|
p->ofs_unit = sbi->segs_per_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p->max_search > MAX_VICTIM_SEARCH)
|
||||||
|
p->max_search = MAX_VICTIM_SEARCH;
|
||||||
|
|
||||||
p->offset = sbi->last_victim[p->gc_mode];
|
p->offset = sbi->last_victim[p->gc_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +311,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||||||
if (cost == max_cost)
|
if (cost == max_cost)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (nsearched++ >= MAX_VICTIM_SEARCH) {
|
if (nsearched++ >= p.max_search) {
|
||||||
sbi->last_victim[p.gc_mode] = segno;
|
sbi->last_victim[p.gc_mode] = segno;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -407,8 +428,7 @@ next_step:
|
|||||||
|
|
||||||
/* set page dirty and write it */
|
/* set page dirty and write it */
|
||||||
if (gc_type == FG_GC) {
|
if (gc_type == FG_GC) {
|
||||||
f2fs_submit_bio(sbi, NODE, true);
|
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||||
wait_on_page_writeback(node_page);
|
|
||||||
set_page_dirty(node_page);
|
set_page_dirty(node_page);
|
||||||
} else {
|
} else {
|
||||||
if (!PageWriteback(node_page))
|
if (!PageWriteback(node_page))
|
||||||
@@ -447,7 +467,7 @@ next_step:
|
|||||||
* as indirect or double indirect node blocks, are given, it must be a caller's
|
* as indirect or double indirect node blocks, are given, it must be a caller's
|
||||||
* bug.
|
* bug.
|
||||||
*/
|
*/
|
||||||
block_t start_bidx_of_node(unsigned int node_ofs)
|
block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi)
|
||||||
{
|
{
|
||||||
unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
|
unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
|
||||||
unsigned int bidx;
|
unsigned int bidx;
|
||||||
@@ -464,7 +484,7 @@ block_t start_bidx_of_node(unsigned int node_ofs)
|
|||||||
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
||||||
bidx = node_ofs - 5 - dec;
|
bidx = node_ofs - 5 - dec;
|
||||||
}
|
}
|
||||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE;
|
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||||
@@ -508,10 +528,7 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
|||||||
} else {
|
} else {
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
|
|
||||||
if (PageWriteback(page)) {
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||||
f2fs_submit_bio(sbi, DATA, true);
|
|
||||||
wait_on_page_writeback(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clear_page_dirty_for_io(page) &&
|
if (clear_page_dirty_for_io(page) &&
|
||||||
S_ISDIR(inode->i_mode)) {
|
S_ISDIR(inode->i_mode)) {
|
||||||
@@ -575,7 +592,6 @@ next_step:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_bidx = start_bidx_of_node(nofs);
|
|
||||||
ofs_in_node = le16_to_cpu(entry->ofs_in_node);
|
ofs_in_node = le16_to_cpu(entry->ofs_in_node);
|
||||||
|
|
||||||
if (phase == 2) {
|
if (phase == 2) {
|
||||||
@@ -583,6 +599,8 @@ next_step:
|
|||||||
if (IS_ERR(inode))
|
if (IS_ERR(inode))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||||
|
|
||||||
data_page = find_data_page(inode,
|
data_page = find_data_page(inode,
|
||||||
start_bidx + ofs_in_node, false);
|
start_bidx + ofs_in_node, false);
|
||||||
if (IS_ERR(data_page))
|
if (IS_ERR(data_page))
|
||||||
@@ -593,6 +611,8 @@ next_step:
|
|||||||
} else {
|
} else {
|
||||||
inode = find_gc_inode(dni.ino, ilist);
|
inode = find_gc_inode(dni.ino, ilist);
|
||||||
if (inode) {
|
if (inode) {
|
||||||
|
start_bidx = start_bidx_of_node(nofs,
|
||||||
|
F2FS_I(inode));
|
||||||
data_page = get_lock_data_page(inode,
|
data_page = get_lock_data_page(inode,
|
||||||
start_bidx + ofs_in_node);
|
start_bidx + ofs_in_node);
|
||||||
if (IS_ERR(data_page))
|
if (IS_ERR(data_page))
|
||||||
|
38
fs/f2fs/gc.h
38
fs/f2fs/gc.h
@@ -13,18 +13,26 @@
|
|||||||
* whether IO subsystem is idle
|
* whether IO subsystem is idle
|
||||||
* or not
|
* or not
|
||||||
*/
|
*/
|
||||||
#define GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */
|
#define DEF_GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */
|
||||||
#define GC_THREAD_MAX_SLEEP_TIME 60000
|
#define DEF_GC_THREAD_MAX_SLEEP_TIME 60000
|
||||||
#define GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
|
#define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
|
||||||
#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */
|
#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */
|
||||||
#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */
|
#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */
|
||||||
|
|
||||||
/* Search max. number of dirty segments to select a victim segment */
|
/* Search max. number of dirty segments to select a victim segment */
|
||||||
#define MAX_VICTIM_SEARCH 20
|
#define MAX_VICTIM_SEARCH 4096 /* covers 8GB */
|
||||||
|
|
||||||
struct f2fs_gc_kthread {
|
struct f2fs_gc_kthread {
|
||||||
struct task_struct *f2fs_gc_task;
|
struct task_struct *f2fs_gc_task;
|
||||||
wait_queue_head_t gc_wait_queue_head;
|
wait_queue_head_t gc_wait_queue_head;
|
||||||
|
|
||||||
|
/* for gc sleep time */
|
||||||
|
unsigned int min_sleep_time;
|
||||||
|
unsigned int max_sleep_time;
|
||||||
|
unsigned int no_gc_sleep_time;
|
||||||
|
|
||||||
|
/* for changing gc mode */
|
||||||
|
unsigned int gc_idle;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct inode_entry {
|
struct inode_entry {
|
||||||
@@ -56,25 +64,25 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
|
|||||||
return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100;
|
return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline long increase_sleep_time(long wait)
|
static inline long increase_sleep_time(struct f2fs_gc_kthread *gc_th, long wait)
|
||||||
{
|
{
|
||||||
if (wait == GC_THREAD_NOGC_SLEEP_TIME)
|
if (wait == gc_th->no_gc_sleep_time)
|
||||||
return wait;
|
return wait;
|
||||||
|
|
||||||
wait += GC_THREAD_MIN_SLEEP_TIME;
|
wait += gc_th->min_sleep_time;
|
||||||
if (wait > GC_THREAD_MAX_SLEEP_TIME)
|
if (wait > gc_th->max_sleep_time)
|
||||||
wait = GC_THREAD_MAX_SLEEP_TIME;
|
wait = gc_th->max_sleep_time;
|
||||||
return wait;
|
return wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline long decrease_sleep_time(long wait)
|
static inline long decrease_sleep_time(struct f2fs_gc_kthread *gc_th, long wait)
|
||||||
{
|
{
|
||||||
if (wait == GC_THREAD_NOGC_SLEEP_TIME)
|
if (wait == gc_th->no_gc_sleep_time)
|
||||||
wait = GC_THREAD_MAX_SLEEP_TIME;
|
wait = gc_th->max_sleep_time;
|
||||||
|
|
||||||
wait -= GC_THREAD_MIN_SLEEP_TIME;
|
wait -= gc_th->min_sleep_time;
|
||||||
if (wait <= GC_THREAD_MIN_SLEEP_TIME)
|
if (wait <= gc_th->min_sleep_time)
|
||||||
wait = GC_THREAD_MIN_SLEEP_TIME;
|
wait = gc_th->min_sleep_time;
|
||||||
return wait;
|
return wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -56,7 +56,7 @@ static int do_read_inode(struct inode *inode)
|
|||||||
if (IS_ERR(node_page))
|
if (IS_ERR(node_page))
|
||||||
return PTR_ERR(node_page);
|
return PTR_ERR(node_page);
|
||||||
|
|
||||||
rn = page_address(node_page);
|
rn = F2FS_NODE(node_page);
|
||||||
ri = &(rn->i);
|
ri = &(rn->i);
|
||||||
|
|
||||||
inode->i_mode = le16_to_cpu(ri->i_mode);
|
inode->i_mode = le16_to_cpu(ri->i_mode);
|
||||||
@@ -85,6 +85,7 @@ static int do_read_inode(struct inode *inode)
|
|||||||
fi->i_advise = ri->i_advise;
|
fi->i_advise = ri->i_advise;
|
||||||
fi->i_pino = le32_to_cpu(ri->i_pino);
|
fi->i_pino = le32_to_cpu(ri->i_pino);
|
||||||
get_extent_info(&fi->ext, ri->i_ext);
|
get_extent_info(&fi->ext, ri->i_ext);
|
||||||
|
get_inline_info(fi, ri);
|
||||||
f2fs_put_page(node_page, 1);
|
f2fs_put_page(node_page, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -151,9 +152,9 @@ void update_inode(struct inode *inode, struct page *node_page)
|
|||||||
struct f2fs_node *rn;
|
struct f2fs_node *rn;
|
||||||
struct f2fs_inode *ri;
|
struct f2fs_inode *ri;
|
||||||
|
|
||||||
wait_on_page_writeback(node_page);
|
f2fs_wait_on_page_writeback(node_page, NODE, false);
|
||||||
|
|
||||||
rn = page_address(node_page);
|
rn = F2FS_NODE(node_page);
|
||||||
ri = &(rn->i);
|
ri = &(rn->i);
|
||||||
|
|
||||||
ri->i_mode = cpu_to_le16(inode->i_mode);
|
ri->i_mode = cpu_to_le16(inode->i_mode);
|
||||||
@@ -164,6 +165,7 @@ void update_inode(struct inode *inode, struct page *node_page)
|
|||||||
ri->i_size = cpu_to_le64(i_size_read(inode));
|
ri->i_size = cpu_to_le64(i_size_read(inode));
|
||||||
ri->i_blocks = cpu_to_le64(inode->i_blocks);
|
ri->i_blocks = cpu_to_le64(inode->i_blocks);
|
||||||
set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext);
|
set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext);
|
||||||
|
set_raw_inline(F2FS_I(inode), ri);
|
||||||
|
|
||||||
ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
|
ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
|
||||||
ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
|
ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
|
||||||
@@ -221,9 +223,6 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||||||
if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE))
|
if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (wbc)
|
|
||||||
f2fs_balance_fs(sbi);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to lock here to prevent from producing dirty node pages
|
* We need to lock here to prevent from producing dirty node pages
|
||||||
* during the urgent cleaning time when runing out of free sections.
|
* during the urgent cleaning time when runing out of free sections.
|
||||||
@@ -231,6 +230,10 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||||||
ilock = mutex_lock_op(sbi);
|
ilock = mutex_lock_op(sbi);
|
||||||
ret = update_inode_page(inode);
|
ret = update_inode_page(inode);
|
||||||
mutex_unlock_op(sbi, ilock);
|
mutex_unlock_op(sbi, ilock);
|
||||||
|
|
||||||
|
if (wbc)
|
||||||
|
f2fs_balance_fs(sbi);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,21 +83,11 @@ static int is_multimedia_file(const unsigned char *s, const char *sub)
|
|||||||
{
|
{
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t sublen = strlen(sub);
|
size_t sublen = strlen(sub);
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (sublen > slen)
|
if (sublen > slen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = memcmp(s + slen - sublen, sub, sublen);
|
return !strncasecmp(s + slen - sublen, sub, sublen);
|
||||||
if (ret) { /* compare upper case */
|
|
||||||
int i;
|
|
||||||
char upper_sub[8];
|
|
||||||
for (i = 0; i < sublen && i < sizeof(upper_sub); i++)
|
|
||||||
upper_sub[i] = toupper(sub[i]);
|
|
||||||
return !memcmp(s + slen - sublen, upper_sub, sublen);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -239,7 +229,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
|||||||
if (!de)
|
if (!de)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
err = check_orphan_space(sbi);
|
err = acquire_orphan_inode(sbi);
|
||||||
if (err) {
|
if (err) {
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
f2fs_put_page(page, 0);
|
f2fs_put_page(page, 0);
|
||||||
@@ -393,7 +383,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
struct inode *old_inode = old_dentry->d_inode;
|
struct inode *old_inode = old_dentry->d_inode;
|
||||||
struct inode *new_inode = new_dentry->d_inode;
|
struct inode *new_inode = new_dentry->d_inode;
|
||||||
struct page *old_dir_page;
|
struct page *old_dir_page;
|
||||||
struct page *old_page;
|
struct page *old_page, *new_page;
|
||||||
struct f2fs_dir_entry *old_dir_entry = NULL;
|
struct f2fs_dir_entry *old_dir_entry = NULL;
|
||||||
struct f2fs_dir_entry *old_entry;
|
struct f2fs_dir_entry *old_entry;
|
||||||
struct f2fs_dir_entry *new_entry;
|
struct f2fs_dir_entry *new_entry;
|
||||||
@@ -415,7 +405,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
ilock = mutex_lock_op(sbi);
|
ilock = mutex_lock_op(sbi);
|
||||||
|
|
||||||
if (new_inode) {
|
if (new_inode) {
|
||||||
struct page *new_page;
|
|
||||||
|
|
||||||
err = -ENOTEMPTY;
|
err = -ENOTEMPTY;
|
||||||
if (old_dir_entry && !f2fs_empty_dir(new_inode))
|
if (old_dir_entry && !f2fs_empty_dir(new_inode))
|
||||||
@@ -427,14 +416,28 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
if (!new_entry)
|
if (!new_entry)
|
||||||
goto out_dir;
|
goto out_dir;
|
||||||
|
|
||||||
|
err = acquire_orphan_inode(sbi);
|
||||||
|
if (err)
|
||||||
|
goto put_out_dir;
|
||||||
|
|
||||||
|
if (update_dent_inode(old_inode, &new_dentry->d_name)) {
|
||||||
|
release_orphan_inode(sbi);
|
||||||
|
goto put_out_dir;
|
||||||
|
}
|
||||||
|
|
||||||
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
|
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
|
||||||
|
|
||||||
new_inode->i_ctime = CURRENT_TIME;
|
new_inode->i_ctime = CURRENT_TIME;
|
||||||
if (old_dir_entry)
|
if (old_dir_entry)
|
||||||
drop_nlink(new_inode);
|
drop_nlink(new_inode);
|
||||||
drop_nlink(new_inode);
|
drop_nlink(new_inode);
|
||||||
|
|
||||||
if (!new_inode->i_nlink)
|
if (!new_inode->i_nlink)
|
||||||
add_orphan_inode(sbi, new_inode->i_ino);
|
add_orphan_inode(sbi, new_inode->i_ino);
|
||||||
|
else
|
||||||
|
release_orphan_inode(sbi);
|
||||||
|
|
||||||
|
update_inode_page(old_inode);
|
||||||
update_inode_page(new_inode);
|
update_inode_page(new_inode);
|
||||||
} else {
|
} else {
|
||||||
err = f2fs_add_link(new_dentry, old_inode);
|
err = f2fs_add_link(new_dentry, old_inode);
|
||||||
@@ -467,6 +470,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
mutex_unlock_op(sbi, ilock);
|
mutex_unlock_op(sbi, ilock);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
put_out_dir:
|
||||||
|
f2fs_put_page(new_page, 1);
|
||||||
out_dir:
|
out_dir:
|
||||||
if (old_dir_entry) {
|
if (old_dir_entry) {
|
||||||
kunmap(old_dir_page);
|
kunmap(old_dir_page);
|
||||||
|
100
fs/f2fs/node.c
100
fs/f2fs/node.c
@@ -315,9 +315,10 @@ cache:
|
|||||||
* The maximum depth is four.
|
* The maximum depth is four.
|
||||||
* Offset[0] will have raw inode offset.
|
* Offset[0] will have raw inode offset.
|
||||||
*/
|
*/
|
||||||
static int get_node_path(long block, int offset[4], unsigned int noffset[4])
|
static int get_node_path(struct f2fs_inode_info *fi, long block,
|
||||||
|
int offset[4], unsigned int noffset[4])
|
||||||
{
|
{
|
||||||
const long direct_index = ADDRS_PER_INODE;
|
const long direct_index = ADDRS_PER_INODE(fi);
|
||||||
const long direct_blks = ADDRS_PER_BLOCK;
|
const long direct_blks = ADDRS_PER_BLOCK;
|
||||||
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
||||||
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
|
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
|
||||||
@@ -405,7 +406,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
|
|||||||
int level, i;
|
int level, i;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
level = get_node_path(index, offset, noffset);
|
level = get_node_path(F2FS_I(dn->inode), index, offset, noffset);
|
||||||
|
|
||||||
nids[0] = dn->inode->i_ino;
|
nids[0] = dn->inode->i_ino;
|
||||||
npage[0] = dn->inode_page;
|
npage[0] = dn->inode_page;
|
||||||
@@ -565,7 +566,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
|
|||||||
return PTR_ERR(page);
|
return PTR_ERR(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
rn = (struct f2fs_node *)page_address(page);
|
rn = F2FS_NODE(page);
|
||||||
if (depth < 3) {
|
if (depth < 3) {
|
||||||
for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) {
|
for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) {
|
||||||
child_nid = le32_to_cpu(rn->in.nid[i]);
|
child_nid = le32_to_cpu(rn->in.nid[i]);
|
||||||
@@ -687,7 +688,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from)
|
|||||||
|
|
||||||
trace_f2fs_truncate_inode_blocks_enter(inode, from);
|
trace_f2fs_truncate_inode_blocks_enter(inode, from);
|
||||||
|
|
||||||
level = get_node_path(from, offset, noffset);
|
level = get_node_path(F2FS_I(inode), from, offset, noffset);
|
||||||
restart:
|
restart:
|
||||||
page = get_node_page(sbi, inode->i_ino);
|
page = get_node_page(sbi, inode->i_ino);
|
||||||
if (IS_ERR(page)) {
|
if (IS_ERR(page)) {
|
||||||
@@ -698,7 +699,7 @@ restart:
|
|||||||
set_new_dnode(&dn, inode, page, NULL, 0);
|
set_new_dnode(&dn, inode, page, NULL, 0);
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
|
|
||||||
rn = page_address(page);
|
rn = F2FS_NODE(page);
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
@@ -771,6 +772,33 @@ fail:
|
|||||||
return err > 0 ? 0 : err;
|
return err > 0 ? 0 : err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int truncate_xattr_node(struct inode *inode, struct page *page)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
|
nid_t nid = F2FS_I(inode)->i_xattr_nid;
|
||||||
|
struct dnode_of_data dn;
|
||||||
|
struct page *npage;
|
||||||
|
|
||||||
|
if (!nid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
npage = get_node_page(sbi, nid);
|
||||||
|
if (IS_ERR(npage))
|
||||||
|
return PTR_ERR(npage);
|
||||||
|
|
||||||
|
F2FS_I(inode)->i_xattr_nid = 0;
|
||||||
|
|
||||||
|
/* need to do checkpoint during fsync */
|
||||||
|
F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||||
|
|
||||||
|
set_new_dnode(&dn, inode, page, npage, nid);
|
||||||
|
|
||||||
|
if (page)
|
||||||
|
dn.inode_page_locked = 1;
|
||||||
|
truncate_node(&dn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Caller should grab and release a mutex by calling mutex_lock_op() and
|
* Caller should grab and release a mutex by calling mutex_lock_op() and
|
||||||
* mutex_unlock_op().
|
* mutex_unlock_op().
|
||||||
@@ -781,22 +809,16 @@ int remove_inode_page(struct inode *inode)
|
|||||||
struct page *page;
|
struct page *page;
|
||||||
nid_t ino = inode->i_ino;
|
nid_t ino = inode->i_ino;
|
||||||
struct dnode_of_data dn;
|
struct dnode_of_data dn;
|
||||||
|
int err;
|
||||||
|
|
||||||
page = get_node_page(sbi, ino);
|
page = get_node_page(sbi, ino);
|
||||||
if (IS_ERR(page))
|
if (IS_ERR(page))
|
||||||
return PTR_ERR(page);
|
return PTR_ERR(page);
|
||||||
|
|
||||||
if (F2FS_I(inode)->i_xattr_nid) {
|
err = truncate_xattr_node(inode, page);
|
||||||
nid_t nid = F2FS_I(inode)->i_xattr_nid;
|
if (err) {
|
||||||
struct page *npage = get_node_page(sbi, nid);
|
f2fs_put_page(page, 1);
|
||||||
|
return err;
|
||||||
if (IS_ERR(npage))
|
|
||||||
return PTR_ERR(npage);
|
|
||||||
|
|
||||||
F2FS_I(inode)->i_xattr_nid = 0;
|
|
||||||
set_new_dnode(&dn, inode, page, npage, nid);
|
|
||||||
dn.inode_page_locked = 1;
|
|
||||||
truncate_node(&dn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 0 is possible, after f2fs_new_inode() is failed */
|
/* 0 is possible, after f2fs_new_inode() is failed */
|
||||||
@@ -833,29 +855,32 @@ struct page *new_node_page(struct dnode_of_data *dn,
|
|||||||
if (!page)
|
if (!page)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
get_node_info(sbi, dn->nid, &old_ni);
|
if (!inc_valid_node_count(sbi, dn->inode, 1)) {
|
||||||
|
err = -ENOSPC;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
SetPageUptodate(page);
|
get_node_info(sbi, dn->nid, &old_ni);
|
||||||
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
|
|
||||||
|
|
||||||
/* Reinitialize old_ni with new node page */
|
/* Reinitialize old_ni with new node page */
|
||||||
BUG_ON(old_ni.blk_addr != NULL_ADDR);
|
BUG_ON(old_ni.blk_addr != NULL_ADDR);
|
||||||
new_ni = old_ni;
|
new_ni = old_ni;
|
||||||
new_ni.ino = dn->inode->i_ino;
|
new_ni.ino = dn->inode->i_ino;
|
||||||
|
|
||||||
if (!inc_valid_node_count(sbi, dn->inode, 1)) {
|
|
||||||
err = -ENOSPC;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
set_node_addr(sbi, &new_ni, NEW_ADDR);
|
set_node_addr(sbi, &new_ni, NEW_ADDR);
|
||||||
|
|
||||||
|
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
|
||||||
set_cold_node(dn->inode, page);
|
set_cold_node(dn->inode, page);
|
||||||
|
SetPageUptodate(page);
|
||||||
|
set_page_dirty(page);
|
||||||
|
|
||||||
|
if (ofs == XATTR_NODE_OFFSET)
|
||||||
|
F2FS_I(dn->inode)->i_xattr_nid = dn->nid;
|
||||||
|
|
||||||
dn->node_page = page;
|
dn->node_page = page;
|
||||||
if (ipage)
|
if (ipage)
|
||||||
update_inode(dn->inode, ipage);
|
update_inode(dn->inode, ipage);
|
||||||
else
|
else
|
||||||
sync_inode_page(dn);
|
sync_inode_page(dn);
|
||||||
set_page_dirty(page);
|
|
||||||
if (ofs == 0)
|
if (ofs == 0)
|
||||||
inc_valid_inode_count(sbi);
|
inc_valid_inode_count(sbi);
|
||||||
|
|
||||||
@@ -916,7 +941,6 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
|
|||||||
f2fs_put_page(apage, 0);
|
f2fs_put_page(apage, 0);
|
||||||
else if (err == LOCKED_PAGE)
|
else if (err == LOCKED_PAGE)
|
||||||
f2fs_put_page(apage, 1);
|
f2fs_put_page(apage, 1);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
|
struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
|
||||||
@@ -1167,9 +1191,9 @@ static int f2fs_write_node_page(struct page *page,
|
|||||||
/*
|
/*
|
||||||
* It is very important to gather dirty pages and write at once, so that we can
|
* It is very important to gather dirty pages and write at once, so that we can
|
||||||
* submit a big bio without interfering other data writes.
|
* submit a big bio without interfering other data writes.
|
||||||
* Be default, 512 pages (2MB), a segment size, is quite reasonable.
|
* Be default, 512 pages (2MB) * 3 node types, is more reasonable.
|
||||||
*/
|
*/
|
||||||
#define COLLECT_DIRTY_NODES 512
|
#define COLLECT_DIRTY_NODES 1536
|
||||||
static int f2fs_write_node_pages(struct address_space *mapping,
|
static int f2fs_write_node_pages(struct address_space *mapping,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
{
|
{
|
||||||
@@ -1187,9 +1211,10 @@ static int f2fs_write_node_pages(struct address_space *mapping,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* if mounting is failed, skip writing node pages */
|
/* if mounting is failed, skip writing node pages */
|
||||||
wbc->nr_to_write = max_hw_blocks(sbi);
|
wbc->nr_to_write = 3 * max_hw_blocks(sbi);
|
||||||
sync_node_pages(sbi, 0, wbc);
|
sync_node_pages(sbi, 0, wbc);
|
||||||
wbc->nr_to_write = nr_to_write - (max_hw_blocks(sbi) - wbc->nr_to_write);
|
wbc->nr_to_write = nr_to_write - (3 * max_hw_blocks(sbi) -
|
||||||
|
wbc->nr_to_write);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1444,6 +1469,9 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
|
|||||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct free_nid *i;
|
struct free_nid *i;
|
||||||
|
|
||||||
|
if (!nid)
|
||||||
|
return;
|
||||||
|
|
||||||
spin_lock(&nm_i->free_nid_list_lock);
|
spin_lock(&nm_i->free_nid_list_lock);
|
||||||
i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
|
i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
|
||||||
BUG_ON(!i || i->state != NID_ALLOC);
|
BUG_ON(!i || i->state != NID_ALLOC);
|
||||||
@@ -1484,8 +1512,8 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)
|
|||||||
SetPageUptodate(ipage);
|
SetPageUptodate(ipage);
|
||||||
fill_node_footer(ipage, ino, ino, 0, true);
|
fill_node_footer(ipage, ino, ino, 0, true);
|
||||||
|
|
||||||
src = (struct f2fs_node *)page_address(page);
|
src = F2FS_NODE(page);
|
||||||
dst = (struct f2fs_node *)page_address(ipage);
|
dst = F2FS_NODE(ipage);
|
||||||
|
|
||||||
memcpy(dst, src, (unsigned long)&src->i.i_ext - (unsigned long)&src->i);
|
memcpy(dst, src, (unsigned long)&src->i.i_ext - (unsigned long)&src->i);
|
||||||
dst->i.i_size = 0;
|
dst->i.i_size = 0;
|
||||||
@@ -1515,8 +1543,8 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
|
|||||||
|
|
||||||
/* alloc temporal page for read node */
|
/* alloc temporal page for read node */
|
||||||
page = alloc_page(GFP_NOFS | __GFP_ZERO);
|
page = alloc_page(GFP_NOFS | __GFP_ZERO);
|
||||||
if (IS_ERR(page))
|
if (!page)
|
||||||
return PTR_ERR(page);
|
return -ENOMEM;
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
|
|
||||||
/* scan the node segment */
|
/* scan the node segment */
|
||||||
@@ -1535,7 +1563,7 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
rn = (struct f2fs_node *)page_address(page);
|
rn = F2FS_NODE(page);
|
||||||
sum_entry->nid = rn->footer.nid;
|
sum_entry->nid = rn->footer.nid;
|
||||||
sum_entry->version = 0;
|
sum_entry->version = 0;
|
||||||
sum_entry->ofs_in_node = 0;
|
sum_entry->ofs_in_node = 0;
|
||||||
|
@@ -155,8 +155,7 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid)
|
|||||||
static inline void fill_node_footer(struct page *page, nid_t nid,
|
static inline void fill_node_footer(struct page *page, nid_t nid,
|
||||||
nid_t ino, unsigned int ofs, bool reset)
|
nid_t ino, unsigned int ofs, bool reset)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(page);
|
struct f2fs_node *rn = F2FS_NODE(page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
if (reset)
|
if (reset)
|
||||||
memset(rn, 0, sizeof(*rn));
|
memset(rn, 0, sizeof(*rn));
|
||||||
rn->footer.nid = cpu_to_le32(nid);
|
rn->footer.nid = cpu_to_le32(nid);
|
||||||
@@ -166,10 +165,8 @@ static inline void fill_node_footer(struct page *page, nid_t nid,
|
|||||||
|
|
||||||
static inline void copy_node_footer(struct page *dst, struct page *src)
|
static inline void copy_node_footer(struct page *dst, struct page *src)
|
||||||
{
|
{
|
||||||
void *src_addr = page_address(src);
|
struct f2fs_node *src_rn = F2FS_NODE(src);
|
||||||
void *dst_addr = page_address(dst);
|
struct f2fs_node *dst_rn = F2FS_NODE(dst);
|
||||||
struct f2fs_node *src_rn = (struct f2fs_node *)src_addr;
|
|
||||||
struct f2fs_node *dst_rn = (struct f2fs_node *)dst_addr;
|
|
||||||
memcpy(&dst_rn->footer, &src_rn->footer, sizeof(struct node_footer));
|
memcpy(&dst_rn->footer, &src_rn->footer, sizeof(struct node_footer));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,45 +174,40 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr)
|
|||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
|
struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
|
||||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||||
void *kaddr = page_address(page);
|
struct f2fs_node *rn = F2FS_NODE(page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
rn->footer.cp_ver = ckpt->checkpoint_ver;
|
rn->footer.cp_ver = ckpt->checkpoint_ver;
|
||||||
rn->footer.next_blkaddr = cpu_to_le32(blkaddr);
|
rn->footer.next_blkaddr = cpu_to_le32(blkaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline nid_t ino_of_node(struct page *node_page)
|
static inline nid_t ino_of_node(struct page *node_page)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(node_page);
|
struct f2fs_node *rn = F2FS_NODE(node_page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
return le32_to_cpu(rn->footer.ino);
|
return le32_to_cpu(rn->footer.ino);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline nid_t nid_of_node(struct page *node_page)
|
static inline nid_t nid_of_node(struct page *node_page)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(node_page);
|
struct f2fs_node *rn = F2FS_NODE(node_page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
return le32_to_cpu(rn->footer.nid);
|
return le32_to_cpu(rn->footer.nid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int ofs_of_node(struct page *node_page)
|
static inline unsigned int ofs_of_node(struct page *node_page)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(node_page);
|
struct f2fs_node *rn = F2FS_NODE(node_page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
unsigned flag = le32_to_cpu(rn->footer.flag);
|
unsigned flag = le32_to_cpu(rn->footer.flag);
|
||||||
return flag >> OFFSET_BIT_SHIFT;
|
return flag >> OFFSET_BIT_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long long cpver_of_node(struct page *node_page)
|
static inline unsigned long long cpver_of_node(struct page *node_page)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(node_page);
|
struct f2fs_node *rn = F2FS_NODE(node_page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
return le64_to_cpu(rn->footer.cp_ver);
|
return le64_to_cpu(rn->footer.cp_ver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline block_t next_blkaddr_of_node(struct page *node_page)
|
static inline block_t next_blkaddr_of_node(struct page *node_page)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(node_page);
|
struct f2fs_node *rn = F2FS_NODE(node_page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
return le32_to_cpu(rn->footer.next_blkaddr);
|
return le32_to_cpu(rn->footer.next_blkaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +229,10 @@ static inline block_t next_blkaddr_of_node(struct page *node_page)
|
|||||||
static inline bool IS_DNODE(struct page *node_page)
|
static inline bool IS_DNODE(struct page *node_page)
|
||||||
{
|
{
|
||||||
unsigned int ofs = ofs_of_node(node_page);
|
unsigned int ofs = ofs_of_node(node_page);
|
||||||
|
|
||||||
|
if (ofs == XATTR_NODE_OFFSET)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
|
if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
|
||||||
ofs == 5 + 2 * NIDS_PER_BLOCK)
|
ofs == 5 + 2 * NIDS_PER_BLOCK)
|
||||||
return false;
|
return false;
|
||||||
@@ -250,7 +246,7 @@ static inline bool IS_DNODE(struct page *node_page)
|
|||||||
|
|
||||||
static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
|
static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
|
||||||
{
|
{
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)page_address(p);
|
struct f2fs_node *rn = F2FS_NODE(p);
|
||||||
|
|
||||||
wait_on_page_writeback(p);
|
wait_on_page_writeback(p);
|
||||||
|
|
||||||
@@ -263,7 +259,8 @@ static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
|
|||||||
|
|
||||||
static inline nid_t get_nid(struct page *p, int off, bool i)
|
static inline nid_t get_nid(struct page *p, int off, bool i)
|
||||||
{
|
{
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)page_address(p);
|
struct f2fs_node *rn = F2FS_NODE(p);
|
||||||
|
|
||||||
if (i)
|
if (i)
|
||||||
return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]);
|
return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]);
|
||||||
return le32_to_cpu(rn->in.nid[off]);
|
return le32_to_cpu(rn->in.nid[off]);
|
||||||
@@ -314,8 +311,7 @@ static inline void clear_cold_data(struct page *page)
|
|||||||
|
|
||||||
static inline int is_node(struct page *page, int type)
|
static inline int is_node(struct page *page, int type)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(page);
|
struct f2fs_node *rn = F2FS_NODE(page);
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
|
|
||||||
return le32_to_cpu(rn->footer.flag) & (1 << type);
|
return le32_to_cpu(rn->footer.flag) & (1 << type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +321,7 @@ static inline int is_node(struct page *page, int type)
|
|||||||
|
|
||||||
static inline void set_cold_node(struct inode *inode, struct page *page)
|
static inline void set_cold_node(struct inode *inode, struct page *page)
|
||||||
{
|
{
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)page_address(page);
|
struct f2fs_node *rn = F2FS_NODE(page);
|
||||||
unsigned int flag = le32_to_cpu(rn->footer.flag);
|
unsigned int flag = le32_to_cpu(rn->footer.flag);
|
||||||
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
@@ -337,7 +333,7 @@ static inline void set_cold_node(struct inode *inode, struct page *page)
|
|||||||
|
|
||||||
static inline void set_mark(struct page *page, int mark, int type)
|
static inline void set_mark(struct page *page, int mark, int type)
|
||||||
{
|
{
|
||||||
struct f2fs_node *rn = (struct f2fs_node *)page_address(page);
|
struct f2fs_node *rn = F2FS_NODE(page);
|
||||||
unsigned int flag = le32_to_cpu(rn->footer.flag);
|
unsigned int flag = le32_to_cpu(rn->footer.flag);
|
||||||
if (mark)
|
if (mark)
|
||||||
flag |= (0x1 << type);
|
flag |= (0x1 << type);
|
||||||
|
@@ -40,8 +40,7 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
|
|||||||
|
|
||||||
static int recover_dentry(struct page *ipage, struct inode *inode)
|
static int recover_dentry(struct page *ipage, struct inode *inode)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(ipage);
|
struct f2fs_node *raw_node = F2FS_NODE(ipage);
|
||||||
struct f2fs_node *raw_node = (struct f2fs_node *)kaddr;
|
|
||||||
struct f2fs_inode *raw_inode = &(raw_node->i);
|
struct f2fs_inode *raw_inode = &(raw_node->i);
|
||||||
nid_t pino = le32_to_cpu(raw_inode->i_pino);
|
nid_t pino = le32_to_cpu(raw_inode->i_pino);
|
||||||
struct f2fs_dir_entry *de;
|
struct f2fs_dir_entry *de;
|
||||||
@@ -93,8 +92,7 @@ out:
|
|||||||
|
|
||||||
static int recover_inode(struct inode *inode, struct page *node_page)
|
static int recover_inode(struct inode *inode, struct page *node_page)
|
||||||
{
|
{
|
||||||
void *kaddr = page_address(node_page);
|
struct f2fs_node *raw_node = F2FS_NODE(node_page);
|
||||||
struct f2fs_node *raw_node = (struct f2fs_node *)kaddr;
|
|
||||||
struct f2fs_inode *raw_inode = &(raw_node->i);
|
struct f2fs_inode *raw_inode = &(raw_node->i);
|
||||||
|
|
||||||
if (!IS_INODE(node_page))
|
if (!IS_INODE(node_page))
|
||||||
@@ -119,7 +117,7 @@ static int recover_inode(struct inode *inode, struct page *node_page)
|
|||||||
|
|
||||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||||
{
|
{
|
||||||
unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver);
|
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||||
struct curseg_info *curseg;
|
struct curseg_info *curseg;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
block_t blkaddr;
|
block_t blkaddr;
|
||||||
@@ -131,8 +129,8 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||||||
|
|
||||||
/* read node page */
|
/* read node page */
|
||||||
page = alloc_page(GFP_F2FS_ZERO);
|
page = alloc_page(GFP_F2FS_ZERO);
|
||||||
if (IS_ERR(page))
|
if (!page)
|
||||||
return PTR_ERR(page);
|
return -ENOMEM;
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -215,6 +213,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||||||
void *kaddr;
|
void *kaddr;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct page *node_page;
|
struct page *node_page;
|
||||||
|
unsigned int offset;
|
||||||
block_t bidx;
|
block_t bidx;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -259,8 +258,8 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||||||
node_page = get_node_page(sbi, nid);
|
node_page = get_node_page(sbi, nid);
|
||||||
if (IS_ERR(node_page))
|
if (IS_ERR(node_page))
|
||||||
return PTR_ERR(node_page);
|
return PTR_ERR(node_page);
|
||||||
bidx = start_bidx_of_node(ofs_of_node(node_page)) +
|
|
||||||
le16_to_cpu(sum.ofs_in_node);
|
offset = ofs_of_node(node_page);
|
||||||
ino = ino_of_node(node_page);
|
ino = ino_of_node(node_page);
|
||||||
f2fs_put_page(node_page, 1);
|
f2fs_put_page(node_page, 1);
|
||||||
|
|
||||||
@@ -269,6 +268,9 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||||||
if (IS_ERR(inode))
|
if (IS_ERR(inode))
|
||||||
return PTR_ERR(inode);
|
return PTR_ERR(inode);
|
||||||
|
|
||||||
|
bidx = start_bidx_of_node(offset, F2FS_I(inode)) +
|
||||||
|
le16_to_cpu(sum.ofs_in_node);
|
||||||
|
|
||||||
truncate_hole(inode, bidx, bidx + 1);
|
truncate_hole(inode, bidx, bidx + 1);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -277,6 +279,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||||||
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||||
struct page *page, block_t blkaddr)
|
struct page *page, block_t blkaddr)
|
||||||
{
|
{
|
||||||
|
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||||
unsigned int start, end;
|
unsigned int start, end;
|
||||||
struct dnode_of_data dn;
|
struct dnode_of_data dn;
|
||||||
struct f2fs_summary sum;
|
struct f2fs_summary sum;
|
||||||
@@ -284,9 +287,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||||||
int err = 0, recovered = 0;
|
int err = 0, recovered = 0;
|
||||||
int ilock;
|
int ilock;
|
||||||
|
|
||||||
start = start_bidx_of_node(ofs_of_node(page));
|
start = start_bidx_of_node(ofs_of_node(page), fi);
|
||||||
if (IS_INODE(page))
|
if (IS_INODE(page))
|
||||||
end = start + ADDRS_PER_INODE;
|
end = start + ADDRS_PER_INODE(fi);
|
||||||
else
|
else
|
||||||
end = start + ADDRS_PER_BLOCK;
|
end = start + ADDRS_PER_BLOCK;
|
||||||
|
|
||||||
@@ -357,7 +360,7 @@ err:
|
|||||||
static int recover_data(struct f2fs_sb_info *sbi,
|
static int recover_data(struct f2fs_sb_info *sbi,
|
||||||
struct list_head *head, int type)
|
struct list_head *head, int type)
|
||||||
{
|
{
|
||||||
unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver);
|
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||||
struct curseg_info *curseg;
|
struct curseg_info *curseg;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@@ -369,7 +372,7 @@ static int recover_data(struct f2fs_sb_info *sbi,
|
|||||||
|
|
||||||
/* read node page */
|
/* read node page */
|
||||||
page = alloc_page(GFP_NOFS | __GFP_ZERO);
|
page = alloc_page(GFP_NOFS | __GFP_ZERO);
|
||||||
if (IS_ERR(page))
|
if (!page)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
|
@@ -117,7 +117,6 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&dirty_i->seglist_lock);
|
mutex_unlock(&dirty_i->seglist_lock);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -261,7 +260,6 @@ static void __add_sum_entry(struct f2fs_sb_info *sbi, int type,
|
|||||||
void *addr = curseg->sum_blk;
|
void *addr = curseg->sum_blk;
|
||||||
addr += curseg->next_blkoff * sizeof(struct f2fs_summary);
|
addr += curseg->next_blkoff * sizeof(struct f2fs_summary);
|
||||||
memcpy(addr, sum, sizeof(struct f2fs_summary));
|
memcpy(addr, sum, sizeof(struct f2fs_summary));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -542,12 +540,9 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
|
|||||||
{
|
{
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
||||||
|
|
||||||
if (force) {
|
if (force)
|
||||||
new_curseg(sbi, type, true);
|
new_curseg(sbi, type, true);
|
||||||
goto out;
|
else if (type == CURSEG_WARM_NODE)
|
||||||
}
|
|
||||||
|
|
||||||
if (type == CURSEG_WARM_NODE)
|
|
||||||
new_curseg(sbi, type, false);
|
new_curseg(sbi, type, false);
|
||||||
else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
|
else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
|
||||||
new_curseg(sbi, type, false);
|
new_curseg(sbi, type, false);
|
||||||
@@ -555,11 +550,9 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
|
|||||||
change_curseg(sbi, type, true);
|
change_curseg(sbi, type, true);
|
||||||
else
|
else
|
||||||
new_curseg(sbi, type, false);
|
new_curseg(sbi, type, false);
|
||||||
out:
|
|
||||||
#ifdef CONFIG_F2FS_STAT_FS
|
#ifdef CONFIG_F2FS_STAT_FS
|
||||||
sbi->segment_count[curseg->alloc_type]++;
|
sbi->segment_count[curseg->alloc_type]++;
|
||||||
#endif
|
#endif
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void allocate_new_segments(struct f2fs_sb_info *sbi)
|
void allocate_new_segments(struct f2fs_sb_info *sbi)
|
||||||
@@ -611,18 +604,12 @@ static void f2fs_end_io_write(struct bio *bio, int err)
|
|||||||
struct bio *f2fs_bio_alloc(struct block_device *bdev, int npages)
|
struct bio *f2fs_bio_alloc(struct block_device *bdev, int npages)
|
||||||
{
|
{
|
||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
struct bio_private *priv;
|
|
||||||
retry:
|
|
||||||
priv = kmalloc(sizeof(struct bio_private), GFP_NOFS);
|
|
||||||
if (!priv) {
|
|
||||||
cond_resched();
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No failure on bio allocation */
|
/* No failure on bio allocation */
|
||||||
bio = bio_alloc(GFP_NOIO, npages);
|
bio = bio_alloc(GFP_NOIO, npages);
|
||||||
bio->bi_bdev = bdev;
|
bio->bi_bdev = bdev;
|
||||||
bio->bi_private = priv;
|
bio->bi_private = NULL;
|
||||||
|
|
||||||
return bio;
|
return bio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,8 +668,17 @@ static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page,
|
|||||||
do_submit_bio(sbi, type, false);
|
do_submit_bio(sbi, type, false);
|
||||||
alloc_new:
|
alloc_new:
|
||||||
if (sbi->bio[type] == NULL) {
|
if (sbi->bio[type] == NULL) {
|
||||||
|
struct bio_private *priv;
|
||||||
|
retry:
|
||||||
|
priv = kmalloc(sizeof(struct bio_private), GFP_NOFS);
|
||||||
|
if (!priv) {
|
||||||
|
cond_resched();
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
sbi->bio[type] = f2fs_bio_alloc(bdev, max_hw_blocks(sbi));
|
sbi->bio[type] = f2fs_bio_alloc(bdev, max_hw_blocks(sbi));
|
||||||
sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr);
|
sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr);
|
||||||
|
sbi->bio[type]->bi_private = priv;
|
||||||
/*
|
/*
|
||||||
* The end_io will be assigned at the sumbission phase.
|
* The end_io will be assigned at the sumbission phase.
|
||||||
* Until then, let bio_add_page() merge consecutive IOs as much
|
* Until then, let bio_add_page() merge consecutive IOs as much
|
||||||
@@ -702,6 +698,16 @@ alloc_new:
|
|||||||
trace_f2fs_submit_write_page(page, blk_addr, type);
|
trace_f2fs_submit_write_page(page, blk_addr, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void f2fs_wait_on_page_writeback(struct page *page,
|
||||||
|
enum page_type type, bool sync)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
|
||||||
|
if (PageWriteback(page)) {
|
||||||
|
f2fs_submit_bio(sbi, type, sync);
|
||||||
|
wait_on_page_writeback(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
|
static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
|
||||||
{
|
{
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
||||||
@@ -1179,7 +1185,6 @@ void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
|
|||||||
{
|
{
|
||||||
if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG))
|
if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG))
|
||||||
write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE);
|
write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type,
|
int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type,
|
||||||
|
@@ -142,6 +142,7 @@ struct victim_sel_policy {
|
|||||||
int alloc_mode; /* LFS or SSR */
|
int alloc_mode; /* LFS or SSR */
|
||||||
int gc_mode; /* GC_CB or GC_GREEDY */
|
int gc_mode; /* GC_CB or GC_GREEDY */
|
||||||
unsigned long *dirty_segmap; /* dirty segment bitmap */
|
unsigned long *dirty_segmap; /* dirty segment bitmap */
|
||||||
|
unsigned int max_search; /* maximum # of segments to search */
|
||||||
unsigned int offset; /* last scanned bitmap offset */
|
unsigned int offset; /* last scanned bitmap offset */
|
||||||
unsigned int ofs_unit; /* bitmap search unit */
|
unsigned int ofs_unit; /* bitmap search unit */
|
||||||
unsigned int min_cost; /* minimum cost */
|
unsigned int min_cost; /* minimum cost */
|
||||||
@@ -453,7 +454,8 @@ static inline int reserved_sections(struct f2fs_sb_info *sbi)
|
|||||||
|
|
||||||
static inline bool need_SSR(struct f2fs_sb_info *sbi)
|
static inline bool need_SSR(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
return (free_sections(sbi) < overprovision_sections(sbi));
|
return ((prefree_segments(sbi) / sbi->segs_per_sec)
|
||||||
|
+ free_sections(sbi) < overprovision_sections(sbi));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
|
static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
|
||||||
@@ -470,7 +472,7 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
|
|||||||
|
|
||||||
static inline int utilization(struct f2fs_sb_info *sbi)
|
static inline int utilization(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
return div_u64(valid_user_blocks(sbi) * 100, sbi->user_block_count);
|
return div_u64((u64)valid_user_blocks(sbi) * 100, sbi->user_block_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
209
fs/f2fs/super.c
209
fs/f2fs/super.c
@@ -18,20 +18,25 @@
|
|||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <linux/exportfs.h>
|
#include <linux/exportfs.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/f2fs_fs.h>
|
#include <linux/f2fs_fs.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
|
||||||
#include "f2fs.h"
|
#include "f2fs.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "segment.h"
|
#include "segment.h"
|
||||||
#include "xattr.h"
|
#include "xattr.h"
|
||||||
|
#include "gc.h"
|
||||||
|
|
||||||
#define CREATE_TRACE_POINTS
|
#define CREATE_TRACE_POINTS
|
||||||
#include <trace/events/f2fs.h>
|
#include <trace/events/f2fs.h>
|
||||||
|
|
||||||
|
static struct proc_dir_entry *f2fs_proc_root;
|
||||||
static struct kmem_cache *f2fs_inode_cachep;
|
static struct kmem_cache *f2fs_inode_cachep;
|
||||||
|
static struct kset *f2fs_kset;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
Opt_gc_background,
|
Opt_gc_background,
|
||||||
@@ -42,6 +47,7 @@ enum {
|
|||||||
Opt_noacl,
|
Opt_noacl,
|
||||||
Opt_active_logs,
|
Opt_active_logs,
|
||||||
Opt_disable_ext_identify,
|
Opt_disable_ext_identify,
|
||||||
|
Opt_inline_xattr,
|
||||||
Opt_err,
|
Opt_err,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,9 +60,117 @@ static match_table_t f2fs_tokens = {
|
|||||||
{Opt_noacl, "noacl"},
|
{Opt_noacl, "noacl"},
|
||||||
{Opt_active_logs, "active_logs=%u"},
|
{Opt_active_logs, "active_logs=%u"},
|
||||||
{Opt_disable_ext_identify, "disable_ext_identify"},
|
{Opt_disable_ext_identify, "disable_ext_identify"},
|
||||||
|
{Opt_inline_xattr, "inline_xattr"},
|
||||||
{Opt_err, NULL},
|
{Opt_err, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Sysfs support for f2fs */
|
||||||
|
struct f2fs_attr {
|
||||||
|
struct attribute attr;
|
||||||
|
ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *);
|
||||||
|
ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *,
|
||||||
|
const char *, size_t);
|
||||||
|
int offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||||
|
struct f2fs_sb_info *sbi, char *buf)
|
||||||
|
{
|
||||||
|
struct f2fs_gc_kthread *gc_kth = sbi->gc_thread;
|
||||||
|
unsigned int *ui;
|
||||||
|
|
||||||
|
if (!gc_kth)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ui = (unsigned int *)(((char *)gc_kth) + a->offset);
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n", *ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t f2fs_sbi_store(struct f2fs_attr *a,
|
||||||
|
struct f2fs_sb_info *sbi,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct f2fs_gc_kthread *gc_kth = sbi->gc_thread;
|
||||||
|
unsigned long t;
|
||||||
|
unsigned int *ui;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (!gc_kth)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ui = (unsigned int *)(((char *)gc_kth) + a->offset);
|
||||||
|
|
||||||
|
ret = kstrtoul(skip_spaces(buf), 0, &t);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
*ui = t;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t f2fs_attr_show(struct kobject *kobj,
|
||||||
|
struct attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
|
||||||
|
s_kobj);
|
||||||
|
struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
|
||||||
|
|
||||||
|
return a->show ? a->show(a, sbi, buf) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||||
|
const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
|
||||||
|
s_kobj);
|
||||||
|
struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
|
||||||
|
|
||||||
|
return a->store ? a->store(a, sbi, buf, len) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void f2fs_sb_release(struct kobject *kobj)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
|
||||||
|
s_kobj);
|
||||||
|
complete(&sbi->s_kobj_unregister);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define F2FS_ATTR_OFFSET(_name, _mode, _show, _store, _elname) \
|
||||||
|
static struct f2fs_attr f2fs_attr_##_name = { \
|
||||||
|
.attr = {.name = __stringify(_name), .mode = _mode }, \
|
||||||
|
.show = _show, \
|
||||||
|
.store = _store, \
|
||||||
|
.offset = offsetof(struct f2fs_gc_kthread, _elname), \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define F2FS_RW_ATTR(name, elname) \
|
||||||
|
F2FS_ATTR_OFFSET(name, 0644, f2fs_sbi_show, f2fs_sbi_store, elname)
|
||||||
|
|
||||||
|
F2FS_RW_ATTR(gc_min_sleep_time, min_sleep_time);
|
||||||
|
F2FS_RW_ATTR(gc_max_sleep_time, max_sleep_time);
|
||||||
|
F2FS_RW_ATTR(gc_no_gc_sleep_time, no_gc_sleep_time);
|
||||||
|
F2FS_RW_ATTR(gc_idle, gc_idle);
|
||||||
|
|
||||||
|
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||||
|
static struct attribute *f2fs_attrs[] = {
|
||||||
|
ATTR_LIST(gc_min_sleep_time),
|
||||||
|
ATTR_LIST(gc_max_sleep_time),
|
||||||
|
ATTR_LIST(gc_no_gc_sleep_time),
|
||||||
|
ATTR_LIST(gc_idle),
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sysfs_ops f2fs_attr_ops = {
|
||||||
|
.show = f2fs_attr_show,
|
||||||
|
.store = f2fs_attr_store,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct kobj_type f2fs_ktype = {
|
||||||
|
.default_attrs = f2fs_attrs,
|
||||||
|
.sysfs_ops = &f2fs_attr_ops,
|
||||||
|
.release = f2fs_sb_release,
|
||||||
|
};
|
||||||
|
|
||||||
void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...)
|
void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
struct va_format vaf;
|
struct va_format vaf;
|
||||||
@@ -126,11 +240,18 @@ static int parse_options(struct super_block *sb, char *options)
|
|||||||
case Opt_nouser_xattr:
|
case Opt_nouser_xattr:
|
||||||
clear_opt(sbi, XATTR_USER);
|
clear_opt(sbi, XATTR_USER);
|
||||||
break;
|
break;
|
||||||
|
case Opt_inline_xattr:
|
||||||
|
set_opt(sbi, INLINE_XATTR);
|
||||||
|
break;
|
||||||
#else
|
#else
|
||||||
case Opt_nouser_xattr:
|
case Opt_nouser_xattr:
|
||||||
f2fs_msg(sb, KERN_INFO,
|
f2fs_msg(sb, KERN_INFO,
|
||||||
"nouser_xattr options not supported");
|
"nouser_xattr options not supported");
|
||||||
break;
|
break;
|
||||||
|
case Opt_inline_xattr:
|
||||||
|
f2fs_msg(sb, KERN_INFO,
|
||||||
|
"inline_xattr options not supported");
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||||
case Opt_noacl:
|
case Opt_noacl:
|
||||||
@@ -180,6 +301,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
|
|||||||
|
|
||||||
set_inode_flag(fi, FI_NEW_INODE);
|
set_inode_flag(fi, FI_NEW_INODE);
|
||||||
|
|
||||||
|
if (test_opt(F2FS_SB(sb), INLINE_XATTR))
|
||||||
|
set_inode_flag(fi, FI_INLINE_XATTR);
|
||||||
|
|
||||||
return &fi->vfs_inode;
|
return &fi->vfs_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +329,6 @@ static int f2fs_drop_inode(struct inode *inode)
|
|||||||
static void f2fs_dirty_inode(struct inode *inode, int flags)
|
static void f2fs_dirty_inode(struct inode *inode, int flags)
|
||||||
{
|
{
|
||||||
set_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
|
set_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void f2fs_i_callback(struct rcu_head *head)
|
static void f2fs_i_callback(struct rcu_head *head)
|
||||||
@@ -223,6 +346,12 @@ static void f2fs_put_super(struct super_block *sb)
|
|||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||||
|
|
||||||
|
if (sbi->s_proc) {
|
||||||
|
remove_proc_entry("segment_info", sbi->s_proc);
|
||||||
|
remove_proc_entry(sb->s_id, f2fs_proc_root);
|
||||||
|
}
|
||||||
|
kobject_del(&sbi->s_kobj);
|
||||||
|
|
||||||
f2fs_destroy_stats(sbi);
|
f2fs_destroy_stats(sbi);
|
||||||
stop_gc_thread(sbi);
|
stop_gc_thread(sbi);
|
||||||
|
|
||||||
@@ -236,6 +365,8 @@ static void f2fs_put_super(struct super_block *sb)
|
|||||||
destroy_segment_manager(sbi);
|
destroy_segment_manager(sbi);
|
||||||
|
|
||||||
kfree(sbi->ckpt);
|
kfree(sbi->ckpt);
|
||||||
|
kobject_put(&sbi->s_kobj);
|
||||||
|
wait_for_completion(&sbi->s_kobj_unregister);
|
||||||
|
|
||||||
sb->s_fs_info = NULL;
|
sb->s_fs_info = NULL;
|
||||||
brelse(sbi->raw_super_buf);
|
brelse(sbi->raw_super_buf);
|
||||||
@@ -325,6 +456,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
|||||||
seq_puts(seq, ",user_xattr");
|
seq_puts(seq, ",user_xattr");
|
||||||
else
|
else
|
||||||
seq_puts(seq, ",nouser_xattr");
|
seq_puts(seq, ",nouser_xattr");
|
||||||
|
if (test_opt(sbi, INLINE_XATTR))
|
||||||
|
seq_puts(seq, ",inline_xattr");
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||||
if (test_opt(sbi, POSIX_ACL))
|
if (test_opt(sbi, POSIX_ACL))
|
||||||
@@ -340,6 +473,36 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int segment_info_seq_show(struct seq_file *seq, void *offset)
|
||||||
|
{
|
||||||
|
struct super_block *sb = seq->private;
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||||
|
unsigned int total_segs = le32_to_cpu(sbi->raw_super->segment_count_main);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < total_segs; i++) {
|
||||||
|
seq_printf(seq, "%u", get_valid_blocks(sbi, i, 1));
|
||||||
|
if (i != 0 && (i % 10) == 0)
|
||||||
|
seq_puts(seq, "\n");
|
||||||
|
else
|
||||||
|
seq_puts(seq, " ");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int segment_info_open_fs(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, segment_info_seq_show, PDE_DATA(inode));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations f2fs_seq_segment_info_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = segment_info_open_fs,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||||
@@ -455,7 +618,7 @@ static const struct export_operations f2fs_export_ops = {
|
|||||||
|
|
||||||
static loff_t max_file_size(unsigned bits)
|
static loff_t max_file_size(unsigned bits)
|
||||||
{
|
{
|
||||||
loff_t result = ADDRS_PER_INODE;
|
loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS);
|
||||||
loff_t leaf_count = ADDRS_PER_BLOCK;
|
loff_t leaf_count = ADDRS_PER_BLOCK;
|
||||||
|
|
||||||
/* two direct node blocks */
|
/* two direct node blocks */
|
||||||
@@ -766,6 +929,13 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
if (f2fs_proc_root)
|
||||||
|
sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root);
|
||||||
|
|
||||||
|
if (sbi->s_proc)
|
||||||
|
proc_create_data("segment_info", S_IRUGO, sbi->s_proc,
|
||||||
|
&f2fs_seq_segment_info_fops, sb);
|
||||||
|
|
||||||
if (test_opt(sbi, DISCARD)) {
|
if (test_opt(sbi, DISCARD)) {
|
||||||
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
||||||
if (!blk_queue_discard(q))
|
if (!blk_queue_discard(q))
|
||||||
@@ -774,6 +944,13 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
"the device does not support discard");
|
"the device does not support discard");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sbi->s_kobj.kset = f2fs_kset;
|
||||||
|
init_completion(&sbi->s_kobj_unregister);
|
||||||
|
err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL,
|
||||||
|
"%s", sb->s_id);
|
||||||
|
if (err)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
stop_gc_thread(sbi);
|
stop_gc_thread(sbi);
|
||||||
@@ -841,29 +1018,49 @@ static int __init init_f2fs_fs(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
err = create_node_manager_caches();
|
err = create_node_manager_caches();
|
||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto free_inodecache;
|
||||||
err = create_gc_caches();
|
err = create_gc_caches();
|
||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto free_node_manager_caches;
|
||||||
err = create_checkpoint_caches();
|
err = create_checkpoint_caches();
|
||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto free_gc_caches;
|
||||||
|
f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj);
|
||||||
|
if (!f2fs_kset) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto free_checkpoint_caches;
|
||||||
|
}
|
||||||
err = register_filesystem(&f2fs_fs_type);
|
err = register_filesystem(&f2fs_fs_type);
|
||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto free_kset;
|
||||||
f2fs_create_root_stats();
|
f2fs_create_root_stats();
|
||||||
|
f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
free_kset:
|
||||||
|
kset_unregister(f2fs_kset);
|
||||||
|
free_checkpoint_caches:
|
||||||
|
destroy_checkpoint_caches();
|
||||||
|
free_gc_caches:
|
||||||
|
destroy_gc_caches();
|
||||||
|
free_node_manager_caches:
|
||||||
|
destroy_node_manager_caches();
|
||||||
|
free_inodecache:
|
||||||
|
destroy_inodecache();
|
||||||
fail:
|
fail:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit exit_f2fs_fs(void)
|
static void __exit exit_f2fs_fs(void)
|
||||||
{
|
{
|
||||||
|
remove_proc_entry("fs/f2fs", NULL);
|
||||||
f2fs_destroy_root_stats();
|
f2fs_destroy_root_stats();
|
||||||
unregister_filesystem(&f2fs_fs_type);
|
unregister_filesystem(&f2fs_fs_type);
|
||||||
destroy_checkpoint_caches();
|
destroy_checkpoint_caches();
|
||||||
destroy_gc_caches();
|
destroy_gc_caches();
|
||||||
destroy_node_manager_caches();
|
destroy_node_manager_caches();
|
||||||
destroy_inodecache();
|
destroy_inodecache();
|
||||||
|
kset_unregister(f2fs_kset);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(init_f2fs_fs)
|
module_init(init_f2fs_fs)
|
||||||
|
291
fs/f2fs/xattr.c
291
fs/f2fs/xattr.c
@@ -246,40 +246,170 @@ static inline const struct xattr_handler *f2fs_xattr_handler(int name_index)
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
int f2fs_getxattr(struct inode *inode, int name_index, const char *name,
|
static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int name_index,
|
||||||
void *buffer, size_t buffer_size)
|
size_t name_len, const char *name)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
|
||||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
|
||||||
struct f2fs_xattr_entry *entry;
|
struct f2fs_xattr_entry *entry;
|
||||||
struct page *page;
|
|
||||||
void *base_addr;
|
|
||||||
int error = 0, found = 0;
|
|
||||||
size_t value_len, name_len;
|
|
||||||
|
|
||||||
if (name == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
name_len = strlen(name);
|
|
||||||
|
|
||||||
if (!fi->i_xattr_nid)
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
page = get_node_page(sbi, fi->i_xattr_nid);
|
|
||||||
if (IS_ERR(page))
|
|
||||||
return PTR_ERR(page);
|
|
||||||
base_addr = page_address(page);
|
|
||||||
|
|
||||||
list_for_each_xattr(entry, base_addr) {
|
list_for_each_xattr(entry, base_addr) {
|
||||||
if (entry->e_name_index != name_index)
|
if (entry->e_name_index != name_index)
|
||||||
continue;
|
continue;
|
||||||
if (entry->e_name_len != name_len)
|
if (entry->e_name_len != name_len)
|
||||||
continue;
|
continue;
|
||||||
if (!memcmp(entry->e_name, name, name_len)) {
|
if (!memcmp(entry->e_name, name, name_len))
|
||||||
found = 1;
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *read_all_xattrs(struct inode *inode, struct page *ipage)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
|
struct f2fs_xattr_header *header;
|
||||||
|
size_t size = PAGE_SIZE, inline_size = 0;
|
||||||
|
void *txattr_addr;
|
||||||
|
|
||||||
|
inline_size = inline_xattr_size(inode);
|
||||||
|
|
||||||
|
txattr_addr = kzalloc(inline_size + size, GFP_KERNEL);
|
||||||
|
if (!txattr_addr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* read from inline xattr */
|
||||||
|
if (inline_size) {
|
||||||
|
struct page *page = NULL;
|
||||||
|
void *inline_addr;
|
||||||
|
|
||||||
|
if (ipage) {
|
||||||
|
inline_addr = inline_xattr_addr(ipage);
|
||||||
|
} else {
|
||||||
|
page = get_node_page(sbi, inode->i_ino);
|
||||||
|
if (IS_ERR(page))
|
||||||
|
goto fail;
|
||||||
|
inline_addr = inline_xattr_addr(page);
|
||||||
|
}
|
||||||
|
memcpy(txattr_addr, inline_addr, inline_size);
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read from xattr node block */
|
||||||
|
if (F2FS_I(inode)->i_xattr_nid) {
|
||||||
|
struct page *xpage;
|
||||||
|
void *xattr_addr;
|
||||||
|
|
||||||
|
/* The inode already has an extended attribute block. */
|
||||||
|
xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid);
|
||||||
|
if (IS_ERR(xpage))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
xattr_addr = page_address(xpage);
|
||||||
|
memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE);
|
||||||
|
f2fs_put_page(xpage, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
header = XATTR_HDR(txattr_addr);
|
||||||
|
|
||||||
|
/* never been allocated xattrs */
|
||||||
|
if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
|
||||||
|
header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
|
||||||
|
header->h_refcount = cpu_to_le32(1);
|
||||||
|
}
|
||||||
|
return txattr_addr;
|
||||||
|
fail:
|
||||||
|
kzfree(txattr_addr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||||
|
void *txattr_addr, struct page *ipage)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
|
size_t inline_size = 0;
|
||||||
|
void *xattr_addr;
|
||||||
|
struct page *xpage;
|
||||||
|
nid_t new_nid = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
inline_size = inline_xattr_size(inode);
|
||||||
|
|
||||||
|
if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid)
|
||||||
|
if (!alloc_nid(sbi, &new_nid))
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
/* write to inline xattr */
|
||||||
|
if (inline_size) {
|
||||||
|
struct page *page = NULL;
|
||||||
|
void *inline_addr;
|
||||||
|
|
||||||
|
if (ipage) {
|
||||||
|
inline_addr = inline_xattr_addr(ipage);
|
||||||
|
} else {
|
||||||
|
page = get_node_page(sbi, inode->i_ino);
|
||||||
|
if (IS_ERR(page)) {
|
||||||
|
alloc_nid_failed(sbi, new_nid);
|
||||||
|
return PTR_ERR(page);
|
||||||
|
}
|
||||||
|
inline_addr = inline_xattr_addr(page);
|
||||||
|
}
|
||||||
|
memcpy(inline_addr, txattr_addr, inline_size);
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
|
|
||||||
|
/* no need to use xattr node block */
|
||||||
|
if (hsize <= inline_size) {
|
||||||
|
err = truncate_xattr_node(inode, ipage);
|
||||||
|
alloc_nid_failed(sbi, new_nid);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
|
||||||
|
/* write to xattr node block */
|
||||||
|
if (F2FS_I(inode)->i_xattr_nid) {
|
||||||
|
xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid);
|
||||||
|
if (IS_ERR(xpage)) {
|
||||||
|
alloc_nid_failed(sbi, new_nid);
|
||||||
|
return PTR_ERR(xpage);
|
||||||
|
}
|
||||||
|
BUG_ON(new_nid);
|
||||||
|
} else {
|
||||||
|
struct dnode_of_data dn;
|
||||||
|
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
||||||
|
xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage);
|
||||||
|
if (IS_ERR(xpage)) {
|
||||||
|
alloc_nid_failed(sbi, new_nid);
|
||||||
|
return PTR_ERR(xpage);
|
||||||
|
}
|
||||||
|
alloc_nid_done(sbi, new_nid);
|
||||||
|
}
|
||||||
|
|
||||||
|
xattr_addr = page_address(xpage);
|
||||||
|
memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE -
|
||||||
|
sizeof(struct node_footer));
|
||||||
|
set_page_dirty(xpage);
|
||||||
|
f2fs_put_page(xpage, 1);
|
||||||
|
|
||||||
|
/* need to checkpoint during fsync */
|
||||||
|
F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f2fs_getxattr(struct inode *inode, int name_index, const char *name,
|
||||||
|
void *buffer, size_t buffer_size)
|
||||||
|
{
|
||||||
|
struct f2fs_xattr_entry *entry;
|
||||||
|
void *base_addr;
|
||||||
|
int error = 0;
|
||||||
|
size_t value_len, name_len;
|
||||||
|
|
||||||
|
if (name == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
name_len = strlen(name);
|
||||||
|
|
||||||
|
base_addr = read_all_xattrs(inode, NULL);
|
||||||
|
if (!base_addr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
entry = __find_xattr(base_addr, name_index, name_len, name);
|
||||||
|
if (IS_XATTR_LAST_ENTRY(entry)) {
|
||||||
error = -ENODATA;
|
error = -ENODATA;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -298,28 +428,21 @@ int f2fs_getxattr(struct inode *inode, int name_index, const char *name,
|
|||||||
error = value_len;
|
error = value_len;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
f2fs_put_page(page, 1);
|
kzfree(base_addr);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
|
||||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
|
||||||
struct f2fs_xattr_entry *entry;
|
struct f2fs_xattr_entry *entry;
|
||||||
struct page *page;
|
|
||||||
void *base_addr;
|
void *base_addr;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
size_t rest = buffer_size;
|
size_t rest = buffer_size;
|
||||||
|
|
||||||
if (!fi->i_xattr_nid)
|
base_addr = read_all_xattrs(inode, NULL);
|
||||||
return 0;
|
if (!base_addr)
|
||||||
|
return -ENOMEM;
|
||||||
page = get_node_page(sbi, fi->i_xattr_nid);
|
|
||||||
if (IS_ERR(page))
|
|
||||||
return PTR_ERR(page);
|
|
||||||
base_addr = page_address(page);
|
|
||||||
|
|
||||||
list_for_each_xattr(entry, base_addr) {
|
list_for_each_xattr(entry, base_addr) {
|
||||||
const struct xattr_handler *handler =
|
const struct xattr_handler *handler =
|
||||||
@@ -342,7 +465,7 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|||||||
}
|
}
|
||||||
error = buffer_size - rest;
|
error = buffer_size - rest;
|
||||||
cleanup:
|
cleanup:
|
||||||
f2fs_put_page(page, 1);
|
kzfree(base_addr);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,14 +474,13 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
|
|||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||||
struct f2fs_xattr_header *header = NULL;
|
|
||||||
struct f2fs_xattr_entry *here, *last;
|
struct f2fs_xattr_entry *here, *last;
|
||||||
struct page *page;
|
|
||||||
void *base_addr;
|
void *base_addr;
|
||||||
int error, found, free, newsize;
|
int found, newsize;
|
||||||
size_t name_len;
|
size_t name_len;
|
||||||
char *pval;
|
|
||||||
int ilock;
|
int ilock;
|
||||||
|
__u32 new_hsize;
|
||||||
|
int error = -ENOMEM;
|
||||||
|
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -368,67 +490,21 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
|
|||||||
|
|
||||||
name_len = strlen(name);
|
name_len = strlen(name);
|
||||||
|
|
||||||
if (name_len > F2FS_NAME_LEN || value_len > MAX_VALUE_LEN)
|
if (name_len > F2FS_NAME_LEN || value_len > MAX_VALUE_LEN(inode))
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
f2fs_balance_fs(sbi);
|
f2fs_balance_fs(sbi);
|
||||||
|
|
||||||
ilock = mutex_lock_op(sbi);
|
ilock = mutex_lock_op(sbi);
|
||||||
|
|
||||||
if (!fi->i_xattr_nid) {
|
base_addr = read_all_xattrs(inode, ipage);
|
||||||
/* Allocate new attribute block */
|
if (!base_addr)
|
||||||
struct dnode_of_data dn;
|
goto exit;
|
||||||
|
|
||||||
if (!alloc_nid(sbi, &fi->i_xattr_nid)) {
|
|
||||||
error = -ENOSPC;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid);
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
|
|
||||||
page = new_node_page(&dn, XATTR_NODE_OFFSET, ipage);
|
|
||||||
if (IS_ERR(page)) {
|
|
||||||
alloc_nid_failed(sbi, fi->i_xattr_nid);
|
|
||||||
fi->i_xattr_nid = 0;
|
|
||||||
error = PTR_ERR(page);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
alloc_nid_done(sbi, fi->i_xattr_nid);
|
|
||||||
base_addr = page_address(page);
|
|
||||||
header = XATTR_HDR(base_addr);
|
|
||||||
header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
|
|
||||||
header->h_refcount = cpu_to_le32(1);
|
|
||||||
} else {
|
|
||||||
/* The inode already has an extended attribute block. */
|
|
||||||
page = get_node_page(sbi, fi->i_xattr_nid);
|
|
||||||
if (IS_ERR(page)) {
|
|
||||||
error = PTR_ERR(page);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
base_addr = page_address(page);
|
|
||||||
header = XATTR_HDR(base_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
|
|
||||||
error = -EIO;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find entry with wanted name. */
|
/* find entry with wanted name. */
|
||||||
found = 0;
|
here = __find_xattr(base_addr, name_index, name_len, name);
|
||||||
list_for_each_xattr(here, base_addr) {
|
|
||||||
if (here->e_name_index != name_index)
|
|
||||||
continue;
|
|
||||||
if (here->e_name_len != name_len)
|
|
||||||
continue;
|
|
||||||
if (!memcmp(here->e_name, name, name_len)) {
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
|
||||||
last = here;
|
last = here;
|
||||||
|
|
||||||
while (!IS_XATTR_LAST_ENTRY(last))
|
while (!IS_XATTR_LAST_ENTRY(last))
|
||||||
@@ -439,22 +515,25 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
|
|||||||
|
|
||||||
/* 1. Check space */
|
/* 1. Check space */
|
||||||
if (value) {
|
if (value) {
|
||||||
/* If value is NULL, it is remove operation.
|
int free;
|
||||||
|
/*
|
||||||
|
* If value is NULL, it is remove operation.
|
||||||
* In case of update operation, we caculate free.
|
* In case of update operation, we caculate free.
|
||||||
*/
|
*/
|
||||||
free = MIN_OFFSET - ((char *)last - (char *)header);
|
free = MIN_OFFSET(inode) - ((char *)last - (char *)base_addr);
|
||||||
if (found)
|
if (found)
|
||||||
free = free - ENTRY_SIZE(here);
|
free = free - ENTRY_SIZE(here);
|
||||||
|
|
||||||
if (free < newsize) {
|
if (free < newsize) {
|
||||||
error = -ENOSPC;
|
error = -ENOSPC;
|
||||||
goto cleanup;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2. Remove old entry */
|
/* 2. Remove old entry */
|
||||||
if (found) {
|
if (found) {
|
||||||
/* If entry is found, remove old entry.
|
/*
|
||||||
|
* If entry is found, remove old entry.
|
||||||
* If not found, remove operation is not needed.
|
* If not found, remove operation is not needed.
|
||||||
*/
|
*/
|
||||||
struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
|
struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
|
||||||
@@ -465,10 +544,15 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
|
|||||||
memset(last, 0, oldsize);
|
memset(last, 0, oldsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_hsize = (char *)last - (char *)base_addr;
|
||||||
|
|
||||||
/* 3. Write new entry */
|
/* 3. Write new entry */
|
||||||
if (value) {
|
if (value) {
|
||||||
/* Before we come here, old entry is removed.
|
char *pval;
|
||||||
* We just write new entry. */
|
/*
|
||||||
|
* Before we come here, old entry is removed.
|
||||||
|
* We just write new entry.
|
||||||
|
*/
|
||||||
memset(last, 0, newsize);
|
memset(last, 0, newsize);
|
||||||
last->e_name_index = name_index;
|
last->e_name_index = name_index;
|
||||||
last->e_name_len = name_len;
|
last->e_name_len = name_len;
|
||||||
@@ -476,26 +560,25 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
|
|||||||
pval = last->e_name + name_len;
|
pval = last->e_name + name_len;
|
||||||
memcpy(pval, value, value_len);
|
memcpy(pval, value, value_len);
|
||||||
last->e_value_size = cpu_to_le16(value_len);
|
last->e_value_size = cpu_to_le16(value_len);
|
||||||
|
new_hsize += newsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_page_dirty(page);
|
error = write_all_xattrs(inode, new_hsize, base_addr, ipage);
|
||||||
f2fs_put_page(page, 1);
|
if (error)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
if (is_inode_flag_set(fi, FI_ACL_MODE)) {
|
if (is_inode_flag_set(fi, FI_ACL_MODE)) {
|
||||||
inode->i_mode = fi->i_acl_mode;
|
inode->i_mode = fi->i_acl_mode;
|
||||||
inode->i_ctime = CURRENT_TIME;
|
inode->i_ctime = CURRENT_TIME;
|
||||||
clear_inode_flag(fi, FI_ACL_MODE);
|
clear_inode_flag(fi, FI_ACL_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ipage)
|
if (ipage)
|
||||||
update_inode(inode, ipage);
|
update_inode(inode, ipage);
|
||||||
else
|
else
|
||||||
update_inode_page(inode);
|
update_inode_page(inode);
|
||||||
mutex_unlock_op(sbi, ilock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
cleanup:
|
|
||||||
f2fs_put_page(page, 1);
|
|
||||||
exit:
|
exit:
|
||||||
mutex_unlock_op(sbi, ilock);
|
mutex_unlock_op(sbi, ilock);
|
||||||
|
kzfree(base_addr);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@@ -51,7 +51,7 @@ struct f2fs_xattr_entry {
|
|||||||
|
|
||||||
#define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr))
|
#define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr))
|
||||||
#define XATTR_ENTRY(ptr) ((struct f2fs_xattr_entry *)(ptr))
|
#define XATTR_ENTRY(ptr) ((struct f2fs_xattr_entry *)(ptr))
|
||||||
#define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr)+1))
|
#define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr) + 1))
|
||||||
#define XATTR_ROUND (3)
|
#define XATTR_ROUND (3)
|
||||||
|
|
||||||
#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND)
|
#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND)
|
||||||
@@ -69,17 +69,16 @@ struct f2fs_xattr_entry {
|
|||||||
!IS_XATTR_LAST_ENTRY(entry);\
|
!IS_XATTR_LAST_ENTRY(entry);\
|
||||||
entry = XATTR_NEXT_ENTRY(entry))
|
entry = XATTR_NEXT_ENTRY(entry))
|
||||||
|
|
||||||
|
#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + PAGE_SIZE - \
|
||||||
|
sizeof(struct node_footer) - sizeof(__u32))
|
||||||
|
|
||||||
#define MIN_OFFSET XATTR_ALIGN(PAGE_SIZE - \
|
#define MAX_VALUE_LEN(i) (MIN_OFFSET(i) - \
|
||||||
sizeof(struct node_footer) - \
|
sizeof(struct f2fs_xattr_header) - \
|
||||||
sizeof(__u32))
|
sizeof(struct f2fs_xattr_entry))
|
||||||
|
|
||||||
#define MAX_VALUE_LEN (MIN_OFFSET - sizeof(struct f2fs_xattr_header) - \
|
|
||||||
sizeof(struct f2fs_xattr_entry))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On-disk structure of f2fs_xattr
|
* On-disk structure of f2fs_xattr
|
||||||
* We use only 1 block for xattr.
|
* We use inline xattrs space + 1 block for xattr.
|
||||||
*
|
*
|
||||||
* +--------------------+
|
* +--------------------+
|
||||||
* | f2fs_xattr_header |
|
* | f2fs_xattr_header |
|
||||||
|
@@ -140,14 +140,24 @@ struct f2fs_extent {
|
|||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define F2FS_NAME_LEN 255
|
#define F2FS_NAME_LEN 255
|
||||||
#define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
|
||||||
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
||||||
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
#define ADDRS_PER_INODE(fi) addrs_per_inode(fi)
|
||||||
|
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||||
|
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
||||||
|
|
||||||
|
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
||||||
|
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
||||||
|
#define NODE_IND1_BLOCK (DEF_ADDRS_PER_INODE + 3)
|
||||||
|
#define NODE_IND2_BLOCK (DEF_ADDRS_PER_INODE + 4)
|
||||||
|
#define NODE_DIND_BLOCK (DEF_ADDRS_PER_INODE + 5)
|
||||||
|
|
||||||
|
#define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */
|
||||||
|
|
||||||
struct f2fs_inode {
|
struct f2fs_inode {
|
||||||
__le16 i_mode; /* file mode */
|
__le16 i_mode; /* file mode */
|
||||||
__u8 i_advise; /* file hints */
|
__u8 i_advise; /* file hints */
|
||||||
__u8 i_reserved; /* reserved */
|
__u8 i_inline; /* file inline flags */
|
||||||
__le32 i_uid; /* user ID */
|
__le32 i_uid; /* user ID */
|
||||||
__le32 i_gid; /* group ID */
|
__le32 i_gid; /* group ID */
|
||||||
__le32 i_links; /* links count */
|
__le32 i_links; /* links count */
|
||||||
@@ -170,7 +180,7 @@ struct f2fs_inode {
|
|||||||
|
|
||||||
struct f2fs_extent i_ext; /* caching a largest extent */
|
struct f2fs_extent i_ext; /* caching a largest extent */
|
||||||
|
|
||||||
__le32 i_addr[ADDRS_PER_INODE]; /* Pointers to data blocks */
|
__le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
|
||||||
|
|
||||||
__le32 i_nid[5]; /* direct(2), indirect(2),
|
__le32 i_nid[5]; /* direct(2), indirect(2),
|
||||||
double_indirect(1) node id */
|
double_indirect(1) node id */
|
||||||
|
Reference in New Issue
Block a user