aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2010-12-26 02:38:43 -0500
committerRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2011-01-10 00:05:46 -0500
commit622daaff0a8975fb5c5b95f24f3234550ba32e92 (patch)
tree5ae5de9044f962471fa2694654134f5b58f73ace
parent27e6c7a3ce29ae5fa5bec4ed5917f8508bfac120 (diff)
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>
-rw-r--r--fs/nilfs2/file.c1
-rw-r--r--fs/nilfs2/inode.c131
-rw-r--r--fs/nilfs2/namei.c1
-rw-r--r--fs/nilfs2/nilfs.h2
-rw-r--r--fs/nilfs2/page.c84
-rw-r--r--fs/nilfs2/page.h3
6 files changed, 222 insertions, 0 deletions
diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c
index c9a30d7ff6f..2f560c9fb80 100644
--- a/fs/nilfs2/file.c
+++ b/fs/nilfs2/file.c
@@ -155,6 +155,7 @@ const struct inode_operations nilfs_file_inode_operations = {
155 .truncate = nilfs_truncate, 155 .truncate = nilfs_truncate,
156 .setattr = nilfs_setattr, 156 .setattr = nilfs_setattr,
157 .permission = nilfs_permission, 157 .permission = nilfs_permission,
158 .fiemap = nilfs_fiemap,
158}; 159};
159 160
160/* end of file */ 161/* end of file */
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 1a546a86d7a..b2a815033ee 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -916,3 +916,134 @@ void nilfs_dirty_inode(struct inode *inode)
916 nilfs_mark_inode_dirty(inode); 916 nilfs_mark_inode_dirty(inode);
917 nilfs_transaction_commit(inode->i_sb); /* never fails */ 917 nilfs_transaction_commit(inode->i_sb); /* never fails */
918} 918}
919
920int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
921 __u64 start, __u64 len)
922{
923 struct the_nilfs *nilfs = NILFS_I_NILFS(inode);
924 __u64 logical = 0, phys = 0, size = 0;
925 __u32 flags = 0;
926 loff_t isize;
927 sector_t blkoff, end_blkoff;
928 sector_t delalloc_blkoff;
929 unsigned long delalloc_blklen;
930 unsigned int blkbits = inode->i_blkbits;
931 int ret, n;
932
933 ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
934 if (ret)
935 return ret;
936
937 mutex_lock(&inode->i_mutex);
938
939 isize = i_size_read(inode);
940
941 blkoff = start >> blkbits;
942 end_blkoff = (start + len - 1) >> blkbits;
943
944 delalloc_blklen = nilfs_find_uncommitted_extent(inode, blkoff,
945 &delalloc_blkoff);
946
947 do {
948 __u64 blkphy;
949 unsigned int maxblocks;
950
951 if (delalloc_blklen && blkoff == delalloc_blkoff) {
952 if (size) {
953 /* End of the current extent */
954 ret = fiemap_fill_next_extent(
955 fieinfo, logical, phys, size, flags);
956 if (ret)
957 break;
958 }
959 if (blkoff > end_blkoff)
960 break;
961
962 flags = FIEMAP_EXTENT_MERGED | FIEMAP_EXTENT_DELALLOC;
963 logical = blkoff << blkbits;
964 phys = 0;
965 size = delalloc_blklen << blkbits;
966
967 blkoff = delalloc_blkoff + delalloc_blklen;
968 delalloc_blklen = nilfs_find_uncommitted_extent(
969 inode, blkoff, &delalloc_blkoff);
970 continue;
971 }
972
973 /*
974 * Limit the number of blocks that we look up so as
975 * not to get into the next delayed allocation extent.
976 */
977 maxblocks = INT_MAX;
978 if (delalloc_blklen)
979 maxblocks = min_t(sector_t, delalloc_blkoff - blkoff,
980 maxblocks);
981 blkphy = 0;
982
983 down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
984 n = nilfs_bmap_lookup_contig(
985 NILFS_I(inode)->i_bmap, blkoff, &blkphy, maxblocks);
986 up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
987
988 if (n < 0) {
989 int past_eof;
990
991 if (unlikely(n != -ENOENT))
992 break; /* error */
993
994 /* HOLE */
995 blkoff++;
996 past_eof = ((blkoff << blkbits) >= isize);
997
998 if (size) {
999 /* End of the current extent */
1000
1001 if (past_eof)
1002 flags |= FIEMAP_EXTENT_LAST;
1003
1004 ret = fiemap_fill_next_extent(
1005 fieinfo, logical, phys, size, flags);
1006 if (ret)
1007 break;
1008 size = 0;
1009 }
1010 if (blkoff > end_blkoff || past_eof)
1011 break;
1012 } else {
1013 if (size) {
1014 if (phys && blkphy << blkbits == phys + size) {
1015 /* The current extent goes on */
1016 size += n << blkbits;
1017 } else {
1018 /* Terminate the current extent */
1019 ret = fiemap_fill_next_extent(
1020 fieinfo, logical, phys, size,
1021 flags);
1022 if (ret || blkoff > end_blkoff)
1023 break;
1024
1025 /* Start another extent */
1026 flags = FIEMAP_EXTENT_MERGED;
1027 logical = blkoff << blkbits;
1028 phys = blkphy << blkbits;
1029 size = n << blkbits;
1030 }
1031 } else {
1032 /* Start a new extent */
1033 flags = FIEMAP_EXTENT_MERGED;
1034 logical = blkoff << blkbits;
1035 phys = blkphy << blkbits;
1036 size = n << blkbits;
1037 }
1038 blkoff += n;
1039 }
1040 cond_resched();
1041 } while (true);
1042
1043 /* If ret is 1 then we just hit the end of the extent array */
1044 if (ret == 1)
1045 ret = 0;
1046
1047 mutex_unlock(&inode->i_mutex);
1048 return ret;
1049}
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index 6e9557ecf16..98034271cd0 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -577,6 +577,7 @@ const struct inode_operations nilfs_dir_inode_operations = {
577 .rename = nilfs_rename, 577 .rename = nilfs_rename,
578 .setattr = nilfs_setattr, 578 .setattr = nilfs_setattr,
579 .permission = nilfs_permission, 579 .permission = nilfs_permission,
580 .fiemap = nilfs_fiemap,
580}; 581};
581 582
582const struct inode_operations nilfs_special_inode_operations = { 583const struct inode_operations nilfs_special_inode_operations = {
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h
index 0ca98823db5..a0e21363e86 100644
--- a/fs/nilfs2/nilfs.h
+++ b/fs/nilfs2/nilfs.h
@@ -264,6 +264,8 @@ extern int nilfs_set_file_dirty(struct nilfs_sb_info *, struct inode *,
264 unsigned); 264 unsigned);
265extern int nilfs_mark_inode_dirty(struct inode *); 265extern int nilfs_mark_inode_dirty(struct inode *);
266extern void nilfs_dirty_inode(struct inode *); 266extern void nilfs_dirty_inode(struct inode *);
267int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
268 __u64 start, __u64 len);
267 269
268/* super.c */ 270/* super.c */
269extern struct inode *nilfs_alloc_inode(struct super_block *); 271extern struct inode *nilfs_alloc_inode(struct super_block *);
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index a6c3c2e817f..48a775ec1d2 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -546,3 +546,87 @@ int __nilfs_clear_page_dirty(struct page *page)
546 } 546 }
547 return TestClearPageDirty(page); 547 return TestClearPageDirty(page);
548} 548}
549
550/**
551 * nilfs_find_uncommitted_extent - find extent of uncommitted data
552 * @inode: inode
553 * @start_blk: start block offset (in)
554 * @blkoff: start offset of the found extent (out)
555 *
556 * This function searches an extent of buffers marked "delayed" which
557 * starts from a block offset equal to or larger than @start_blk. If
558 * such an extent was found, this will store the start offset in
559 * @blkoff and return its length in blocks. Otherwise, zero is
560 * returned.
561 */
562unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
563 sector_t start_blk,
564 sector_t *blkoff)
565{
566 unsigned int i;
567 pgoff_t index;
568 unsigned int nblocks_in_page;
569 unsigned long length = 0;
570 sector_t b;
571 struct pagevec pvec;
572 struct page *page;
573
574 if (inode->i_mapping->nrpages == 0)
575 return 0;
576
577 index = start_blk >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
578 nblocks_in_page = 1U << (PAGE_CACHE_SHIFT - inode->i_blkbits);
579
580 pagevec_init(&pvec, 0);
581
582repeat:
583 pvec.nr = find_get_pages_contig(inode->i_mapping, index, PAGEVEC_SIZE,
584 pvec.pages);
585 if (pvec.nr == 0)
586 return length;
587
588 if (length > 0 && pvec.pages[0]->index > index)
589 goto out;
590
591 b = pvec.pages[0]->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
592 i = 0;
593 do {
594 page = pvec.pages[i];
595
596 lock_page(page);
597 if (page_has_buffers(page)) {
598 struct buffer_head *bh, *head;
599
600 bh = head = page_buffers(page);
601 do {
602 if (b < start_blk)
603 continue;
604 if (buffer_delay(bh)) {
605 if (length == 0)
606 *blkoff = b;
607 length++;
608 } else if (length > 0) {
609 goto out_locked;
610 }
611 } while (++b, bh = bh->b_this_page, bh != head);
612 } else {
613 if (length > 0)
614 goto out_locked;
615
616 b += nblocks_in_page;
617 }
618 unlock_page(page);
619
620 } while (++i < pagevec_count(&pvec));
621
622 index = page->index + 1;
623 pagevec_release(&pvec);
624 cond_resched();
625 goto repeat;
626
627out_locked:
628 unlock_page(page);
629out:
630 pagevec_release(&pvec);
631 return length;
632}
diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
index fb9e8a8a203..622df27cd89 100644
--- a/fs/nilfs2/page.h
+++ b/fs/nilfs2/page.h
@@ -66,6 +66,9 @@ void nilfs_mapping_init(struct address_space *mapping,
66 struct backing_dev_info *bdi, 66 struct backing_dev_info *bdi,
67 const struct address_space_operations *aops); 67 const struct address_space_operations *aops);
68unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned); 68unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned);
69unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
70 sector_t start_blk,
71 sector_t *blkoff);
69 72
70#define NILFS_PAGE_BUG(page, m, a...) \ 73#define NILFS_PAGE_BUG(page, m, a...) \
71 do { nilfs_page_bug(page); BUG(); } while (0) 74 do { nilfs_page_bug(page); BUG(); } while (0)