This function is called by xfs_seek_data() and xfs_seek_hole() to find the desired offset from page cache. Signed-off-by: Jie Liu <jeff.liu@xxxxxxxxxx> Reviewed-by: Mark Tinguely <tinguely@xxxxxxx> Reviewed-by: Christoph Hellwig <hch@xxxxxx> Reviewed-by: Dave Chinner <dchinner@xxxxxxxxxx> --- fs/xfs/xfs_file.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 208 insertions(+), 0 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 98642cf..69965a4 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -36,6 +36,7 @@ #include <linux/dcache.h> #include <linux/falloc.h> +#include <linux/pagevec.h> static const struct vm_operations_struct xfs_file_vm_ops; @@ -966,6 +967,213 @@ xfs_vm_page_mkwrite( return block_page_mkwrite(vma, vmf, xfs_get_blocks); } +/* + * This type is designed to indicate the type of offset we would like + * to search from page cache for either xfs_seek_data() or xfs_seek_hole(). + */ +enum { + HOLE_OFF = 0, + DATA_OFF, +}; + +/* + * This routine is called to find out and return a data or hole offset + * from the page cache for unwritten extents according to the desired + * type for xfs_seek_data() or xfs_seek_hole(). + * + * The argument offset is used to tell where we start to search from the + * page cache, and it will be filled with the desired type of offset if + * it was found, or it will keep unchanged. map is used to figure out + * the end points of the range to lookup pages. + */ +STATIC bool +xfs_find_get_desired_pgoff( + struct inode *inode, + struct xfs_bmbt_irec *map, + unsigned int type, + loff_t *offset) +{ + struct xfs_inode *ip = XFS_I(inode); + struct xfs_mount *mp = ip->i_mount; + struct pagevec pvec; + pgoff_t index; + pgoff_t end; + loff_t endoff; + loff_t coff = *offset; /* current search offset */ + bool found = false; + + pagevec_init(&pvec, 0); + index = XFS_FSB_TO_B(mp, XFS_B_TO_FSBT(mp, coff)) >> PAGE_CACHE_SHIFT; + + /* The end offset to search for */ + endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); + end = endoff >> PAGE_CACHE_SHIFT; + + do { + unsigned int i; + unsigned nr_pages; + int want = min_t(pgoff_t, end - index, + (pgoff_t)PAGEVEC_SIZE - 1) + 1; + + nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, + want); + /* + * No page mapped into given range. If we are searching holes + * and if this is the first time we got into the loop, it means + * that the given offset is landed in a hole and return ture. + * + * If we have already stepped through some block buffers to find + * holes but those buffers are all contains data, in this case, + * the current search offset is already aligned to block buffer + * unit boundary and pointed to the end of the last mapped page. + * If it's location is less than the end range given for search, + * that means there should be a hole between them, so return the + * current search offset if we are searching hole. + */ + if (nr_pages == 0) { + if (type == HOLE_OFF) { + if (coff == *offset) + found = true; + if (coff < endoff) { + found = true; + *offset = coff; + } + } + /* Search data but nothing found */ + break; + } + + /* + * At least we found one page. If this the first time we step + * into the loop, and if the first page index offset is greater + * than the given search offset, a hole was found, return true + * if we are searching holes. + */ + if ((type == HOLE_OFF) && (coff == *offset)) { + if (coff < pvec.pages[0]->index << PAGE_CACHE_SHIFT) { + found = true; + break; + } + } + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + struct buffer_head *bh; + struct buffer_head *head; + xfs_fileoff_t last; + + /* + * Page index is out of range, we need to deal with + * hole search condition in paticular if that is the + * desired type for the lookup. + * stepping into the block buffer checkup, it probably + * means that there is no page mapped at all in the + * specified range to search, so we found a hole. + * If we have already done some block buffer checking + * and found one or more data buffers before, in this + * case, the coff is already updated and it point to + * the end of the last data buffer, so the left range + * behind it might be a hole. In either case, we will + * return the coff to indicate a hole's location because + * it must be greater than or equal to the search start. + */ + if (page->index > end) { + if (type == HOLE_OFF && coff < endoff) { + *offset = coff; + found = true; + } + goto out; + } + + if (!trylock_page(page)) + goto out; + + if (!page_has_buffers(page)) { + unlock_page(page); + continue; + } + + last = XFS_B_TO_FSBT(mp, + page->index << PAGE_CACHE_SHIFT); + bh = head = page_buffers(page); + do { + off_t lastoff = 0; + + /* + * The 1st block buffer offset in current page. + */ + lastoff = XFS_FSB_TO_B(mp, last); + /* + * An extent in XFS_EXT_UNWRITTEN has disk + * blocks already mapped to it, but no data + * has been committed to them yet. If it has + * dirty data in the page cache it can be + * identified by having BH_Unwritten set in + * each buffers. Also, the buffer head state + * might be in BH_Uptodate too if the buffer + * writeback procedure was fired, we have to + * check it up as well. + */ + if (buffer_unwritten(bh) || + buffer_uptodate(bh)) { + /* + * Found a data buffer and we are + * searching data, great. + */ + if (type == DATA_OFF) + found = true; + } else { + /* + * Nothing was found and we are + * searching holes, great. + */ + if (type == HOLE_OFF) + found = true; + } + if (found) { + /* + * Return if we found the desired + * page offset. + */ + *offset = max_t(loff_t, coff, lastoff); + unlock_page(page); + goto out; + } + /* + * We either searching data but nothing was + * found, or searching hole but found a data + * block buffer. In either case, probably the + * next block buffer is what we are desired, + * so that we need to round up the current + * offset to it. + */ + coff = round_up(lastoff + 1, bh->b_size); + last++; + } while ((bh = bh->b_this_page) != head); + unlock_page(page); + } + + /* + * If the number of returned pages less than our desired, + * there should no more pages mapped, search done. + */ + if (nr_pages < want) { + if (type == HOLE_OFF) { + *offset = coff; + found = true; + } + break; + } + + index = pvec.pages[i - 1]->index + 1; + pagevec_release(&pvec); + } while (index < end); + +out: + pagevec_release(&pvec); + return found; +} + STATIC loff_t xfs_seek_data( struct file *file, -- 1.7.4.1 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs