Commit 622daaff0a8975fb5c5b95f24f3234550ba32e92
1 parent
27e6c7a3ce
Exists in
master
and in
7 other branches
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>
Showing 6 changed files with 222 additions and 0 deletions Side-by-side Diff
fs/nilfs2/file.c
fs/nilfs2/inode.c
... | ... | @@ -916,4 +916,135 @@ |
916 | 916 | nilfs_mark_inode_dirty(inode); |
917 | 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 | +} |
fs/nilfs2/namei.c
fs/nilfs2/nilfs.h
... | ... | @@ -264,6 +264,8 @@ |
264 | 264 | unsigned); |
265 | 265 | extern int nilfs_mark_inode_dirty(struct inode *); |
266 | 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 | 270 | /* super.c */ |
269 | 271 | extern struct inode *nilfs_alloc_inode(struct super_block *); |
fs/nilfs2/page.c
... | ... | @@ -546,4 +546,88 @@ |
546 | 546 | } |
547 | 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 | +} |
fs/nilfs2/page.h
... | ... | @@ -66,6 +66,9 @@ |
66 | 66 | struct backing_dev_info *bdi, |
67 | 67 | const struct address_space_operations *aops); |
68 | 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 | 73 | #define NILFS_PAGE_BUG(page, m, a...) \ |
71 | 74 | do { nilfs_page_bug(page); BUG(); } while (0) |