mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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
					
				
					 6 changed files with 222 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -155,6 +155,7 @@ const struct inode_operations nilfs_file_inode_operations = {
 | 
			
		|||
	.truncate	= nilfs_truncate,
 | 
			
		||||
	.setattr	= nilfs_setattr,
 | 
			
		||||
	.permission     = nilfs_permission,
 | 
			
		||||
	.fiemap		= nilfs_fiemap,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* end of file */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -916,3 +916,134 @@ void nilfs_dirty_inode(struct inode *inode)
 | 
			
		|||
	nilfs_mark_inode_dirty(inode);
 | 
			
		||||
	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,
 | 
			
		||||
	.setattr	= nilfs_setattr,
 | 
			
		||||
	.permission	= nilfs_permission,
 | 
			
		||||
	.fiemap		= nilfs_fiemap,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
extern int nilfs_mark_inode_dirty(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 */
 | 
			
		||||
extern struct inode *nilfs_alloc_inode(struct super_block *);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -546,3 +546,87 @@ int __nilfs_clear_page_dirty(struct page *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,
 | 
			
		||||
			const struct address_space_operations *aops);
 | 
			
		||||
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...) \
 | 
			
		||||
	do { nilfs_page_bug(page); BUG(); } while (0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue