diff options
author | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2010-12-26 02:38:43 -0500 |
---|---|---|
committer | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2011-01-10 00:05:46 -0500 |
commit | 622daaff0a8975fb5c5b95f24f3234550ba32e92 (patch) | |
tree | 5ae5de9044f962471fa2694654134f5b58f73ace /fs | |
parent | 27e6c7a3ce29ae5fa5bec4ed5917f8508bfac120 (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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nilfs2/file.c | 1 | ||||
-rw-r--r-- | fs/nilfs2/inode.c | 131 | ||||
-rw-r--r-- | fs/nilfs2/namei.c | 1 | ||||
-rw-r--r-- | fs/nilfs2/nilfs.h | 2 | ||||
-rw-r--r-- | fs/nilfs2/page.c | 84 | ||||
-rw-r--r-- | fs/nilfs2/page.h | 3 |
6 files changed, 222 insertions, 0 deletions
diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index c9a30d7ff6fc..2f560c9fb808 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 1a546a86d7a7..b2a815033ee3 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 | |||
920 | int 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 6e9557ecf161..98034271cd02 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 | ||
582 | const struct inode_operations nilfs_special_inode_operations = { | 583 | const struct inode_operations nilfs_special_inode_operations = { |
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 0ca98823db59..a0e21363e865 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); |
265 | extern int nilfs_mark_inode_dirty(struct inode *); | 265 | extern int nilfs_mark_inode_dirty(struct inode *); |
266 | extern void nilfs_dirty_inode(struct inode *); | 266 | extern void nilfs_dirty_inode(struct inode *); |
267 | int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, | ||
268 | __u64 start, __u64 len); | ||
267 | 269 | ||
268 | /* super.c */ | 270 | /* super.c */ |
269 | extern struct inode *nilfs_alloc_inode(struct super_block *); | 271 | extern struct inode *nilfs_alloc_inode(struct super_block *); |
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index a6c3c2e817f8..48a775ec1d2a 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 | */ | ||
562 | unsigned 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 | |||
582 | repeat: | ||
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 | |||
627 | out_locked: | ||
628 | unlock_page(page); | ||
629 | out: | ||
630 | pagevec_release(&pvec); | ||
631 | return length; | ||
632 | } | ||
diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index fb9e8a8a2038..622df27cd891 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); |
68 | unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned); | 68 | unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned); |
69 | unsigned 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) |