nilfs2: fiemap support
This adds fiemap to nilfs. Two new functions, nilfs_fiemap and nilfs_find_uncommitted_extent are added. nilfs_fiemap() implements the fiemap inode operation, and nilfs_find_uncommitted_extent() helps to get a range of data blocks whose physical location has not been determined. nilfs_fiemap() collects extent information by looping through nilfs_bmap_lookup_contig and nilfs_find_uncommitted_extent routines. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
This commit is contained in:
parent
27e6c7a3ce
commit
622daaff0a
@ -155,6 +155,7 @@ const struct inode_operations nilfs_file_inode_operations = {
|
|||||||
.truncate = nilfs_truncate,
|
.truncate = nilfs_truncate,
|
||||||
.setattr = nilfs_setattr,
|
.setattr = nilfs_setattr,
|
||||||
.permission = nilfs_permission,
|
.permission = nilfs_permission,
|
||||||
|
.fiemap = nilfs_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* end of file */
|
/* end of file */
|
||||||
|
@ -916,3 +916,134 @@ void nilfs_dirty_inode(struct inode *inode)
|
|||||||
nilfs_mark_inode_dirty(inode);
|
nilfs_mark_inode_dirty(inode);
|
||||||
nilfs_transaction_commit(inode->i_sb); /* never fails */
|
nilfs_transaction_commit(inode->i_sb); /* never fails */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||||
|
__u64 start, __u64 len)
|
||||||
|
{
|
||||||
|
struct the_nilfs *nilfs = NILFS_I_NILFS(inode);
|
||||||
|
__u64 logical = 0, phys = 0, size = 0;
|
||||||
|
__u32 flags = 0;
|
||||||
|
loff_t isize;
|
||||||
|
sector_t blkoff, end_blkoff;
|
||||||
|
sector_t delalloc_blkoff;
|
||||||
|
unsigned long delalloc_blklen;
|
||||||
|
unsigned int blkbits = inode->i_blkbits;
|
||||||
|
int ret, n;
|
||||||
|
|
||||||
|
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
|
||||||
|
isize = i_size_read(inode);
|
||||||
|
|
||||||
|
blkoff = start >> blkbits;
|
||||||
|
end_blkoff = (start + len - 1) >> blkbits;
|
||||||
|
|
||||||
|
delalloc_blklen = nilfs_find_uncommitted_extent(inode, blkoff,
|
||||||
|
&delalloc_blkoff);
|
||||||
|
|
||||||
|
do {
|
||||||
|
__u64 blkphy;
|
||||||
|
unsigned int maxblocks;
|
||||||
|
|
||||||
|
if (delalloc_blklen && blkoff == delalloc_blkoff) {
|
||||||
|
if (size) {
|
||||||
|
/* End of the current extent */
|
||||||
|
ret = fiemap_fill_next_extent(
|
||||||
|
fieinfo, logical, phys, size, flags);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (blkoff > end_blkoff)
|
||||||
|
break;
|
||||||
|
|
||||||
|
flags = FIEMAP_EXTENT_MERGED | FIEMAP_EXTENT_DELALLOC;
|
||||||
|
logical = blkoff << blkbits;
|
||||||
|
phys = 0;
|
||||||
|
size = delalloc_blklen << blkbits;
|
||||||
|
|
||||||
|
blkoff = delalloc_blkoff + delalloc_blklen;
|
||||||
|
delalloc_blklen = nilfs_find_uncommitted_extent(
|
||||||
|
inode, blkoff, &delalloc_blkoff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Limit the number of blocks that we look up so as
|
||||||
|
* not to get into the next delayed allocation extent.
|
||||||
|
*/
|
||||||
|
maxblocks = INT_MAX;
|
||||||
|
if (delalloc_blklen)
|
||||||
|
maxblocks = min_t(sector_t, delalloc_blkoff - blkoff,
|
||||||
|
maxblocks);
|
||||||
|
blkphy = 0;
|
||||||
|
|
||||||
|
down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
|
||||||
|
n = nilfs_bmap_lookup_contig(
|
||||||
|
NILFS_I(inode)->i_bmap, blkoff, &blkphy, maxblocks);
|
||||||
|
up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
int past_eof;
|
||||||
|
|
||||||
|
if (unlikely(n != -ENOENT))
|
||||||
|
break; /* error */
|
||||||
|
|
||||||
|
/* HOLE */
|
||||||
|
blkoff++;
|
||||||
|
past_eof = ((blkoff << blkbits) >= isize);
|
||||||
|
|
||||||
|
if (size) {
|
||||||
|
/* End of the current extent */
|
||||||
|
|
||||||
|
if (past_eof)
|
||||||
|
flags |= FIEMAP_EXTENT_LAST;
|
||||||
|
|
||||||
|
ret = fiemap_fill_next_extent(
|
||||||
|
fieinfo, logical, phys, size, flags);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
if (blkoff > end_blkoff || past_eof)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (size) {
|
||||||
|
if (phys && blkphy << blkbits == phys + size) {
|
||||||
|
/* The current extent goes on */
|
||||||
|
size += n << blkbits;
|
||||||
|
} else {
|
||||||
|
/* Terminate the current extent */
|
||||||
|
ret = fiemap_fill_next_extent(
|
||||||
|
fieinfo, logical, phys, size,
|
||||||
|
flags);
|
||||||
|
if (ret || blkoff > end_blkoff)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Start another extent */
|
||||||
|
flags = FIEMAP_EXTENT_MERGED;
|
||||||
|
logical = blkoff << blkbits;
|
||||||
|
phys = blkphy << blkbits;
|
||||||
|
size = n << blkbits;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Start a new extent */
|
||||||
|
flags = FIEMAP_EXTENT_MERGED;
|
||||||
|
logical = blkoff << blkbits;
|
||||||
|
phys = blkphy << blkbits;
|
||||||
|
size = n << blkbits;
|
||||||
|
}
|
||||||
|
blkoff += n;
|
||||||
|
}
|
||||||
|
cond_resched();
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
/* If ret is 1 then we just hit the end of the extent array */
|
||||||
|
if (ret == 1)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -577,6 +577,7 @@ const struct inode_operations nilfs_dir_inode_operations = {
|
|||||||
.rename = nilfs_rename,
|
.rename = nilfs_rename,
|
||||||
.setattr = nilfs_setattr,
|
.setattr = nilfs_setattr,
|
||||||
.permission = nilfs_permission,
|
.permission = nilfs_permission,
|
||||||
|
.fiemap = nilfs_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations nilfs_special_inode_operations = {
|
const struct inode_operations nilfs_special_inode_operations = {
|
||||||
|
@ -264,6 +264,8 @@ extern int nilfs_set_file_dirty(struct nilfs_sb_info *, struct inode *,
|
|||||||
unsigned);
|
unsigned);
|
||||||
extern int nilfs_mark_inode_dirty(struct inode *);
|
extern int nilfs_mark_inode_dirty(struct inode *);
|
||||||
extern void nilfs_dirty_inode(struct inode *);
|
extern void nilfs_dirty_inode(struct inode *);
|
||||||
|
int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||||
|
__u64 start, __u64 len);
|
||||||
|
|
||||||
/* super.c */
|
/* super.c */
|
||||||
extern struct inode *nilfs_alloc_inode(struct super_block *);
|
extern struct inode *nilfs_alloc_inode(struct super_block *);
|
||||||
|
@ -546,3 +546,87 @@ int __nilfs_clear_page_dirty(struct page *page)
|
|||||||
}
|
}
|
||||||
return TestClearPageDirty(page);
|
return TestClearPageDirty(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nilfs_find_uncommitted_extent - find extent of uncommitted data
|
||||||
|
* @inode: inode
|
||||||
|
* @start_blk: start block offset (in)
|
||||||
|
* @blkoff: start offset of the found extent (out)
|
||||||
|
*
|
||||||
|
* This function searches an extent of buffers marked "delayed" which
|
||||||
|
* starts from a block offset equal to or larger than @start_blk. If
|
||||||
|
* such an extent was found, this will store the start offset in
|
||||||
|
* @blkoff and return its length in blocks. Otherwise, zero is
|
||||||
|
* returned.
|
||||||
|
*/
|
||||||
|
unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
|
||||||
|
sector_t start_blk,
|
||||||
|
sector_t *blkoff)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
pgoff_t index;
|
||||||
|
unsigned int nblocks_in_page;
|
||||||
|
unsigned long length = 0;
|
||||||
|
sector_t b;
|
||||||
|
struct pagevec pvec;
|
||||||
|
struct page *page;
|
||||||
|
|
||||||
|
if (inode->i_mapping->nrpages == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
index = start_blk >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
||||||
|
nblocks_in_page = 1U << (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
||||||
|
|
||||||
|
pagevec_init(&pvec, 0);
|
||||||
|
|
||||||
|
repeat:
|
||||||
|
pvec.nr = find_get_pages_contig(inode->i_mapping, index, PAGEVEC_SIZE,
|
||||||
|
pvec.pages);
|
||||||
|
if (pvec.nr == 0)
|
||||||
|
return length;
|
||||||
|
|
||||||
|
if (length > 0 && pvec.pages[0]->index > index)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
b = pvec.pages[0]->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
page = pvec.pages[i];
|
||||||
|
|
||||||
|
lock_page(page);
|
||||||
|
if (page_has_buffers(page)) {
|
||||||
|
struct buffer_head *bh, *head;
|
||||||
|
|
||||||
|
bh = head = page_buffers(page);
|
||||||
|
do {
|
||||||
|
if (b < start_blk)
|
||||||
|
continue;
|
||||||
|
if (buffer_delay(bh)) {
|
||||||
|
if (length == 0)
|
||||||
|
*blkoff = b;
|
||||||
|
length++;
|
||||||
|
} else if (length > 0) {
|
||||||
|
goto out_locked;
|
||||||
|
}
|
||||||
|
} while (++b, bh = bh->b_this_page, bh != head);
|
||||||
|
} else {
|
||||||
|
if (length > 0)
|
||||||
|
goto out_locked;
|
||||||
|
|
||||||
|
b += nblocks_in_page;
|
||||||
|
}
|
||||||
|
unlock_page(page);
|
||||||
|
|
||||||
|
} while (++i < pagevec_count(&pvec));
|
||||||
|
|
||||||
|
index = page->index + 1;
|
||||||
|
pagevec_release(&pvec);
|
||||||
|
cond_resched();
|
||||||
|
goto repeat;
|
||||||
|
|
||||||
|
out_locked:
|
||||||
|
unlock_page(page);
|
||||||
|
out:
|
||||||
|
pagevec_release(&pvec);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
@ -66,6 +66,9 @@ void nilfs_mapping_init(struct address_space *mapping,
|
|||||||
struct backing_dev_info *bdi,
|
struct backing_dev_info *bdi,
|
||||||
const struct address_space_operations *aops);
|
const struct address_space_operations *aops);
|
||||||
unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned);
|
unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned);
|
||||||
|
unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
|
||||||
|
sector_t start_blk,
|
||||||
|
sector_t *blkoff);
|
||||||
|
|
||||||
#define NILFS_PAGE_BUG(page, m, a...) \
|
#define NILFS_PAGE_BUG(page, m, a...) \
|
||||||
do { nilfs_page_bug(page); BUG(); } while (0)
|
do { nilfs_page_bug(page); BUG(); } while (0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user