Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10ce4444 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Nathan Scott
Browse files

[XFS] use pagevec lookups This reduces the time spend in the radix tree


lookups and avoids unessecary look roundtrips.

SGI-PV: 947118
SGI-Modid: xfs-linux-melb:xfs-kern:203823a

Signed-off-by: default avatarChristoph Hellwig <hch@sgi.com>
Signed-off-by: default avatarNathan Scott <nathans@sgi.com>
parent 78539fdf
Loading
Loading
Loading
Loading
+88 −56
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include "xfs_rw.h"
#include "xfs_iomap.h"
#include <linux/mpage.h>
#include <linux/pagevec.h>
#include <linux/writeback.h>

STATIC void xfs_count_page_state(struct page *, int *, int *, int *);
@@ -501,18 +502,13 @@ xfs_map_at_offset(
 */
STATIC unsigned int
xfs_probe_unmapped_page(
	struct address_space	*mapping,
	pgoff_t			index,
	struct page		*page,
	unsigned int		pg_offset)
{
	struct page		*page;
	int			ret = 0;

	page = find_trylock_page(mapping, index);
	if (!page)
		return 0;
	if (PageWriteback(page))
		goto out;
		return 0;

	if (page->mapping && PageDirty(page)) {
		if (page_has_buffers(page)) {
@@ -530,8 +526,6 @@ xfs_probe_unmapped_page(
			ret = PAGE_CACHE_SIZE;
	}

out:
	unlock_page(page);
	return ret;
}

@@ -542,59 +536,75 @@ xfs_probe_unmapped_cluster(
	struct buffer_head	*bh,
	struct buffer_head	*head)
{
	size_t			len, total = 0;
	struct pagevec		pvec;
	pgoff_t			tindex, tlast, tloff;
	unsigned int		pg_offset;
	struct address_space	*mapping = inode->i_mapping;
	size_t			total = 0;
	int			done = 0, i;

	/* First sum forwards in this page */
	do {
		if (buffer_mapped(bh))
			break;
			return total;
		total += bh->b_size;
	} while ((bh = bh->b_this_page) != head);

	/* If we reached the end of the page, sum forwards in
	 * following pages.
	 */
	if (bh == head) {
	/* if we reached the end of the page, sum forwards in following pages */
	tlast = i_size_read(inode) >> PAGE_CACHE_SHIFT;
	tindex = startpage->index + 1;

	/* Prune this back to avoid pathological behavior */
	tloff = min(tlast, startpage->index + 64);
		for (tindex = startpage->index + 1; tindex < tloff; tindex++) {
			len = xfs_probe_unmapped_page(mapping, tindex,
							PAGE_CACHE_SIZE);
			if (!len)
				return total;
			total += len;

	pagevec_init(&pvec, 0);
	while (!done && tindex <= tloff) {
		unsigned len = min_t(pgoff_t, PAGEVEC_SIZE, tlast - tindex + 1);

		if (!pagevec_lookup(&pvec, inode->i_mapping, tindex, len))
			break;

		for (i = 0; i < pagevec_count(&pvec); i++) {
			struct page *page = pvec.pages[i];
			size_t pg_offset, len = 0;

			if (tindex == tlast) {
				pg_offset =
				    i_size_read(inode) & (PAGE_CACHE_SIZE - 1);
				if (!pg_offset)
					break;
			} else
				pg_offset = PAGE_CACHE_SIZE;

			if (page->index == tindex && !TestSetPageLocked(page)) {
				len = xfs_probe_unmapped_page(page, pg_offset);
				unlock_page(page);
			}

			if (!len) {
				done = 1;
				break;
			}
		if (tindex == tlast &&
		    (pg_offset = i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) {
			total += xfs_probe_unmapped_page(mapping,
							tindex, pg_offset);

			total += len;
		}

		pagevec_release(&pvec);
		cond_resched();
	}

	return total;
}

/*
 * Probe for a given page (index) in the inode and test if it is suitable
 * for writing as part of an unwritten or delayed allocate extent.
 * Returns page locked and with an extra reference count if so, else NULL.
 * Test if a given page is suitable for writing as part of an unwritten
 * or delayed allocate extent.
 */
STATIC struct page *
xfs_probe_delayed_page(
	struct inode		*inode,
	pgoff_t			index,
STATIC int
xfs_is_delayed_page(
	struct page		*page,
	unsigned int		type)
{
	struct page		*page;

	page = find_trylock_page(inode->i_mapping, index);
	if (!page)
		return NULL;
	if (PageWriteback(page))
		goto out;
		return 0;

	if (page->mapping && page_has_buffers(page)) {
		struct buffer_head	*bh, *head;
@@ -611,12 +621,10 @@ xfs_probe_delayed_page(
		} while ((bh = bh->b_this_page) != head);

		if (acceptable)
			return page;
			return 1;
	}

out:
	unlock_page(page);
	return NULL;
	return 0;
}

/*
@@ -629,10 +637,10 @@ STATIC int
xfs_convert_page(
	struct inode		*inode,
	struct page		*page,
	loff_t			tindex,
	xfs_iomap_t		*iomapp,
	xfs_ioend_t		**ioendp,
	struct writeback_control *wbc,
	void			*private,
	int			startio,
	int			all_bh)
{
@@ -644,6 +652,17 @@ xfs_convert_page(
	int			len, page_dirty;
	int			count = 0, done = 0, uptodate = 1;

	if (page->index != tindex)
		goto fail;
	if (TestSetPageLocked(page))
		goto fail;
	if (PageWriteback(page))
		goto fail_unlock_page;
	if (page->mapping != inode->i_mapping)
		goto fail_unlock_page;
	if (!xfs_is_delayed_page(page, (*ioendp)->io_type))
		goto fail_unlock_page;

	end_offset = (i_size_read(inode) & (PAGE_CACHE_SIZE - 1));

	/*
@@ -715,6 +734,10 @@ xfs_convert_page(
	}

	return done;
 fail_unlock_page:
	unlock_page(page);
 fail:
	return 1;
}

/*
@@ -732,16 +755,25 @@ xfs_cluster_write(
	int			all_bh,
	pgoff_t			tlast)
{
	struct page		*page;
	unsigned int		type = (*ioendp)->io_type;
	int			done;
	struct pagevec		pvec;
	int			done = 0, i;

	pagevec_init(&pvec, 0);
	while (!done && tindex <= tlast) {
		unsigned len = min_t(pgoff_t, PAGEVEC_SIZE, tlast - tindex + 1);

		if (!pagevec_lookup(&pvec, inode->i_mapping, tindex, len))
			break;

	for (done = 0; tindex <= tlast && !done; tindex++) {
		page = xfs_probe_delayed_page(inode, tindex, type);
		if (!page)
		for (i = 0; i < pagevec_count(&pvec); i++) {
			done = xfs_convert_page(inode, pvec.pages[i], tindex++,
					iomapp, ioendp, wbc, startio, all_bh);
			if (done)
				break;
		done = xfs_convert_page(inode, page, iomapp, ioendp,
						wbc, NULL, startio, all_bh);
		}

		pagevec_release(&pvec);
		cond_resched();
	}
}