On 07/26/2012 04:56 PM, Jeff Liu wrote: > 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 | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 files changed, 203 insertions(+), 0 deletions(-) > > diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c > index 98642cf..43f5e61 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,208 @@ 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) > + break; Just found an issue here, should check the 'nr_pages < want' condition for searching hole, I'll re-send this patch set a little while. Sorry for the noise! -Jeff > + > + 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, _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs