Commit 78525c74 authored by David Howells's avatar David Howells
Browse files

netfs, 9p, afs, ceph: Use folios



Convert the netfs helper library to use folios throughout, convert the 9p
and afs filesystems to use folios in their file I/O paths and convert the
ceph filesystem to use just enough folios to compile.

With these changes, afs passes -g quick xfstests.

Changes
=======
ver #5:
 - Got rid of folio_end{io,_read,_write}() and inlined the stuff it does
   instead (Willy decided he didn't want this after all).

ver #4:
 - Fixed a bug in afs_redirty_page() whereby it didn't set the next page
   index in the loop and returned too early.
 - Simplified a check in v9fs_vfs_write_folio_locked()[1].
 - Undid a change to afs_symlink_readpage()[1].
 - Used offset_in_folio() in afs_write_end()[1].
 - Changed from using page_endio() to folio_end{io,_read,_write}()[1].

ver #2:
 - Add 9p foliation.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Tested-by: default avatarJeff Layton <jlayton@kernel.org>
Tested-by: default avatarDominique Martinet <asmadeus@codewreck.org>
Tested-by: default avatar <kafs-testing@auristor.com>
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Dominique Martinet <asmadeus@codewreck.org>
cc: v9fs-developer@lists.sourceforge.net
cc: linux-afs@lists.infradead.org
cc: ceph-devel@vger.kernel.org
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/YYKa3bfQZxK5/wDN@casper.infradead.org/ [1]
Link: https://lore.kernel.org/r/2408234.1628687271@warthog.procyon.org.uk/ # rfc
Link: https://lore.kernel.org/r/162877311459.3085614.10601478228012245108.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/162981153551.1901565.3124454657133703341.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/163005745264.2472992.9852048135392188995.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163584187452.4023316.500389675405550116.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/163649328026.309189.1124218109373941936.stgit@warthog.procyon.org.uk/ # v4
Link: https://lore.kernel.org/r/163657852454.834781.9265101983152100556.stgit@warthog.procyon.org.uk/ # v5
parent 452c472e
Loading
Loading
Loading
Loading
+46 −37
Original line number Diff line number Diff line
@@ -108,7 +108,9 @@ static const struct netfs_read_request_ops v9fs_req_ops = {
 */
static int v9fs_vfs_readpage(struct file *file, struct page *page)
{
	return netfs_readpage(file, page, &v9fs_req_ops, NULL);
	struct folio *folio = page_folio(page);

	return netfs_readpage(file, folio, &v9fs_req_ops, NULL);
}

/**
@@ -130,13 +132,15 @@ static void v9fs_vfs_readahead(struct readahead_control *ractl)

static int v9fs_release_page(struct page *page, gfp_t gfp)
{
	if (PagePrivate(page))
	struct folio *folio = page_folio(page);

	if (folio_test_private(folio))
		return 0;
#ifdef CONFIG_9P_FSCACHE
	if (PageFsCache(page)) {
	if (folio_test_fscache(folio)) {
		if (!(gfp & __GFP_DIRECT_RECLAIM) || !(gfp & __GFP_FS))
			return 0;
		wait_on_page_fscache(page);
		folio_wait_fscache(folio);
	}
#endif
	return 1;
@@ -152,55 +156,58 @@ static int v9fs_release_page(struct page *page, gfp_t gfp)
static void v9fs_invalidate_page(struct page *page, unsigned int offset,
				 unsigned int length)
{
	wait_on_page_fscache(page);
	struct folio *folio = page_folio(page);

	folio_wait_fscache(folio);
}

static int v9fs_vfs_writepage_locked(struct page *page)
static int v9fs_vfs_write_folio_locked(struct folio *folio)
{
	struct inode *inode = page->mapping->host;
	struct inode *inode = folio_inode(folio);
	struct v9fs_inode *v9inode = V9FS_I(inode);
	loff_t start = page_offset(page);
	loff_t size = i_size_read(inode);
	loff_t start = folio_pos(folio);
	loff_t i_size = i_size_read(inode);
	struct iov_iter from;
	int err, len;
	size_t len = folio_size(folio);
	int err;

	if (start >= i_size)
		return 0; /* Simultaneous truncation occurred */

	if (page->index == size >> PAGE_SHIFT)
		len = size & ~PAGE_MASK;
	else
		len = PAGE_SIZE;
	len = min_t(loff_t, i_size - start, len);

	iov_iter_xarray(&from, WRITE, &page->mapping->i_pages, start, len);
	iov_iter_xarray(&from, WRITE, &folio_mapping(folio)->i_pages, start, len);

	/* We should have writeback_fid always set */
	BUG_ON(!v9inode->writeback_fid);

	set_page_writeback(page);
	folio_start_writeback(folio);

	p9_client_write(v9inode->writeback_fid, start, &from, &err);

	end_page_writeback(page);
	folio_end_writeback(folio);
	return err;
}

static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
{
	struct folio *folio = page_folio(page);
	int retval;

	p9_debug(P9_DEBUG_VFS, "page %p\n", page);
	p9_debug(P9_DEBUG_VFS, "folio %p\n", folio);

	retval = v9fs_vfs_writepage_locked(page);
	retval = v9fs_vfs_write_folio_locked(folio);
	if (retval < 0) {
		if (retval == -EAGAIN) {
			redirty_page_for_writepage(wbc, page);
			folio_redirty_for_writepage(wbc, folio);
			retval = 0;
		} else {
			SetPageError(page);
			mapping_set_error(page->mapping, retval);
			mapping_set_error(folio_mapping(folio), retval);
		}
	} else
		retval = 0;

	unlock_page(page);
	folio_unlock(folio);
	return retval;
}

@@ -213,14 +220,15 @@ static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)

static int v9fs_launder_page(struct page *page)
{
	struct folio *folio = page_folio(page);
	int retval;

	if (clear_page_dirty_for_io(page)) {
		retval = v9fs_vfs_writepage_locked(page);
	if (folio_clear_dirty_for_io(folio)) {
		retval = v9fs_vfs_write_folio_locked(folio);
		if (retval)
			return retval;
	}
	wait_on_page_fscache(page);
	folio_wait_fscache(folio);
	return 0;
}

@@ -265,10 +273,10 @@ v9fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)

static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
			    loff_t pos, unsigned int len, unsigned int flags,
			    struct page **pagep, void **fsdata)
			    struct page **subpagep, void **fsdata)
{
	int retval;
	struct page *page;
	struct folio *folio;
	struct v9fs_inode *v9inode = V9FS_I(mapping->host);

	p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping);
@@ -279,31 +287,32 @@ static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
	 * file.  We need to do this before we get a lock on the page in case
	 * there's more than one writer competing for the same cache block.
	 */
	retval = netfs_write_begin(filp, mapping, pos, len, flags, &page, fsdata,
	retval = netfs_write_begin(filp, mapping, pos, len, flags, &folio, fsdata,
				   &v9fs_req_ops, NULL);
	if (retval < 0)
		return retval;

	*pagep = find_subpage(page, pos / PAGE_SIZE);
	*subpagep = &folio->page;
	return retval;
}

static int v9fs_write_end(struct file *filp, struct address_space *mapping,
			  loff_t pos, unsigned int len, unsigned int copied,
			  struct page *page, void *fsdata)
			  struct page *subpage, void *fsdata)
{
	loff_t last_pos = pos + copied;
	struct inode *inode = page->mapping->host;
	struct folio *folio = page_folio(subpage);
	struct inode *inode = mapping->host;

	p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping);

	if (!PageUptodate(page)) {
	if (!folio_test_uptodate(folio)) {
		if (unlikely(copied < len)) {
			copied = 0;
			goto out;
		}

		SetPageUptodate(page);
		folio_mark_uptodate(folio);
	}

	/*
@@ -314,10 +323,10 @@ static int v9fs_write_end(struct file *filp, struct address_space *mapping,
		inode_add_bytes(inode, last_pos - inode->i_size);
		i_size_write(inode, last_pos);
	}
	set_page_dirty(page);
	folio_mark_dirty(folio);
out:
	unlock_page(page);
	put_page(page);
	folio_unlock(folio);
	folio_put(folio);

	return copied;
}
+10 −10
Original line number Diff line number Diff line
@@ -528,13 +528,13 @@ static vm_fault_t
v9fs_vm_page_mkwrite(struct vm_fault *vmf)
{
	struct v9fs_inode *v9inode;
	struct page *page = vmf->page;
	struct folio *folio = page_folio(vmf->page);
	struct file *filp = vmf->vma->vm_file;
	struct inode *inode = file_inode(filp);


	p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
		 page, (unsigned long)filp->private_data);
	p9_debug(P9_DEBUG_VFS, "folio %p fid %lx\n",
		 folio, (unsigned long)filp->private_data);

	v9inode = V9FS_I(inode);

@@ -542,24 +542,24 @@ v9fs_vm_page_mkwrite(struct vm_fault *vmf)
	 * be modified.  We then assume the entire page will need writing back.
	 */
#ifdef CONFIG_9P_FSCACHE
	if (PageFsCache(page) &&
	    wait_on_page_fscache_killable(page) < 0)
		return VM_FAULT_RETRY;
	if (folio_test_fscache(folio) &&
	    folio_wait_fscache_killable(folio) < 0)
		return VM_FAULT_NOPAGE;
#endif

	/* Update file times before taking page lock */
	file_update_time(filp);

	BUG_ON(!v9inode->writeback_fid);
	if (lock_page_killable(page) < 0)
	if (folio_lock_killable(folio) < 0)
		return VM_FAULT_RETRY;
	if (page->mapping != inode->i_mapping)
	if (folio_mapping(folio) != inode->i_mapping)
		goto out_unlock;
	wait_for_stable_page(page);
	folio_wait_stable(folio);

	return VM_FAULT_LOCKED;
out_unlock:
	unlock_page(page);
	folio_unlock(folio);
	return VM_FAULT_NOPAGE;
}

+39 −31
Original line number Diff line number Diff line
@@ -324,21 +324,24 @@ static int afs_symlink_readpage(struct file *file, struct page *page)
{
	struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
	struct afs_read *fsreq;
	struct folio *folio = page_folio(page);
	int ret;

	fsreq = afs_alloc_read(GFP_NOFS);
	if (!fsreq)
		return -ENOMEM;

	fsreq->pos	= page->index * PAGE_SIZE;
	fsreq->len	= PAGE_SIZE;
	fsreq->pos	= folio_pos(folio);
	fsreq->len	= folio_size(folio);
	fsreq->vnode	= vnode;
	fsreq->iter	= &fsreq->def_iter;
	iov_iter_xarray(&fsreq->def_iter, READ, &page->mapping->i_pages,
			fsreq->pos, fsreq->len);

	ret = afs_fetch_data(fsreq->vnode, fsreq);
	page_endio(page, false, ret);
	if (ret == 0)
		SetPageUptodate(page);
	unlock_page(page);
	return ret;
}

@@ -362,7 +365,7 @@ static int afs_begin_cache_operation(struct netfs_read_request *rreq)
}

static int afs_check_write_begin(struct file *file, loff_t pos, unsigned len,
				 struct page *page, void **_fsdata)
				 struct folio *folio, void **_fsdata)
{
	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));

@@ -385,7 +388,9 @@ const struct netfs_read_request_ops afs_req_ops = {

static int afs_readpage(struct file *file, struct page *page)
{
	return netfs_readpage(file, page, &afs_req_ops, NULL);
	struct folio *folio = page_folio(page);

	return netfs_readpage(file, folio, &afs_req_ops, NULL);
}

static void afs_readahead(struct readahead_control *ractl)
@@ -397,29 +402,29 @@ static void afs_readahead(struct readahead_control *ractl)
 * Adjust the dirty region of the page on truncation or full invalidation,
 * getting rid of the markers altogether if the region is entirely invalidated.
 */
static void afs_invalidate_dirty(struct page *page, unsigned int offset,
static void afs_invalidate_dirty(struct folio *folio, unsigned int offset,
				 unsigned int length)
{
	struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
	struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio));
	unsigned long priv;
	unsigned int f, t, end = offset + length;

	priv = page_private(page);
	priv = (unsigned long)folio_get_private(folio);

	/* we clean up only if the entire page is being invalidated */
	if (offset == 0 && length == thp_size(page))
	if (offset == 0 && length == folio_size(folio))
		goto full_invalidate;

	 /* If the page was dirtied by page_mkwrite(), the PTE stays writable
	  * and we don't get another notification to tell us to expand it
	  * again.
	  */
	if (afs_is_page_dirty_mmapped(priv))
	if (afs_is_folio_dirty_mmapped(priv))
		return;

	/* We may need to shorten the dirty region */
	f = afs_page_dirty_from(page, priv);
	t = afs_page_dirty_to(page, priv);
	f = afs_folio_dirty_from(folio, priv);
	t = afs_folio_dirty_to(folio, priv);

	if (t <= offset || f >= end)
		return; /* Doesn't overlap */
@@ -437,17 +442,17 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset,
	if (f == t)
		goto undirty;

	priv = afs_page_dirty(page, f, t);
	set_page_private(page, priv);
	trace_afs_page_dirty(vnode, tracepoint_string("trunc"), page);
	priv = afs_folio_dirty(folio, f, t);
	folio_change_private(folio, (void *)priv);
	trace_afs_folio_dirty(vnode, tracepoint_string("trunc"), folio);
	return;

undirty:
	trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page);
	clear_page_dirty_for_io(page);
	trace_afs_folio_dirty(vnode, tracepoint_string("undirty"), folio);
	folio_clear_dirty_for_io(folio);
full_invalidate:
	trace_afs_page_dirty(vnode, tracepoint_string("inval"), page);
	detach_page_private(page);
	trace_afs_folio_dirty(vnode, tracepoint_string("inval"), folio);
	folio_detach_private(folio);
}

/*
@@ -458,14 +463,16 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset,
static void afs_invalidatepage(struct page *page, unsigned int offset,
			       unsigned int length)
{
	_enter("{%lu},%u,%u", page->index, offset, length);
	struct folio *folio = page_folio(page);

	_enter("{%lu},%u,%u", folio_index(folio), offset, length);

	BUG_ON(!PageLocked(page));

	if (PagePrivate(page))
		afs_invalidate_dirty(page, offset, length);
		afs_invalidate_dirty(folio, offset, length);

	wait_on_page_fscache(page);
	folio_wait_fscache(folio);
	_leave("");
}

@@ -475,30 +482,31 @@ static void afs_invalidatepage(struct page *page, unsigned int offset,
 */
static int afs_releasepage(struct page *page, gfp_t gfp_flags)
{
	struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
	struct folio *folio = page_folio(page);
	struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio));

	_enter("{{%llx:%llu}[%lu],%lx},%x",
	       vnode->fid.vid, vnode->fid.vnode, page->index, page->flags,
	       vnode->fid.vid, vnode->fid.vnode, folio_index(folio), folio->flags,
	       gfp_flags);

	/* deny if page is being written to the cache and the caller hasn't
	 * elected to wait */
#ifdef CONFIG_AFS_FSCACHE
	if (PageFsCache(page)) {
	if (folio_test_fscache(folio)) {
		if (!(gfp_flags & __GFP_DIRECT_RECLAIM) || !(gfp_flags & __GFP_FS))
			return false;
		wait_on_page_fscache(page);
		folio_wait_fscache(folio);
	}
#endif

	if (PagePrivate(page)) {
		trace_afs_page_dirty(vnode, tracepoint_string("rel"), page);
		detach_page_private(page);
	if (folio_test_private(folio)) {
		trace_afs_folio_dirty(vnode, tracepoint_string("rel"), folio);
		folio_detach_private(folio);
	}

	/* indicate that the page can be released */
	/* Indicate that the folio can be released */
	_leave(" = T");
	return 1;
	return true;
}

static void afs_add_open_mmap(struct afs_vnode *vnode)
+23 −23
Original line number Diff line number Diff line
@@ -876,59 +876,59 @@ struct afs_vnode_cache_aux {
} __packed;

/*
 * We use page->private to hold the amount of the page that we've written to,
 * We use folio->private to hold the amount of the folio that we've written to,
 * splitting the field into two parts.  However, we need to represent a range
 * 0...PAGE_SIZE, so we reduce the resolution if the size of the page
 * 0...FOLIO_SIZE, so we reduce the resolution if the size of the folio
 * exceeds what we can encode.
 */
#ifdef CONFIG_64BIT
#define __AFS_PAGE_PRIV_MASK	0x7fffffffUL
#define __AFS_PAGE_PRIV_SHIFT	32
#define __AFS_PAGE_PRIV_MMAPPED	0x80000000UL
#define __AFS_FOLIO_PRIV_MASK		0x7fffffffUL
#define __AFS_FOLIO_PRIV_SHIFT		32
#define __AFS_FOLIO_PRIV_MMAPPED	0x80000000UL
#else
#define __AFS_PAGE_PRIV_MASK	0x7fffUL
#define __AFS_PAGE_PRIV_SHIFT	16
#define __AFS_PAGE_PRIV_MMAPPED	0x8000UL
#define __AFS_FOLIO_PRIV_MASK		0x7fffUL
#define __AFS_FOLIO_PRIV_SHIFT		16
#define __AFS_FOLIO_PRIV_MMAPPED	0x8000UL
#endif

static inline unsigned int afs_page_dirty_resolution(struct page *page)
static inline unsigned int afs_folio_dirty_resolution(struct folio *folio)
{
	int shift = thp_order(page) + PAGE_SHIFT - (__AFS_PAGE_PRIV_SHIFT - 1);
	int shift = folio_shift(folio) - (__AFS_FOLIO_PRIV_SHIFT - 1);
	return (shift > 0) ? shift : 0;
}

static inline size_t afs_page_dirty_from(struct page *page, unsigned long priv)
static inline size_t afs_folio_dirty_from(struct folio *folio, unsigned long priv)
{
	unsigned long x = priv & __AFS_PAGE_PRIV_MASK;
	unsigned long x = priv & __AFS_FOLIO_PRIV_MASK;

	/* The lower bound is inclusive */
	return x << afs_page_dirty_resolution(page);
	return x << afs_folio_dirty_resolution(folio);
}

static inline size_t afs_page_dirty_to(struct page *page, unsigned long priv)
static inline size_t afs_folio_dirty_to(struct folio *folio, unsigned long priv)
{
	unsigned long x = (priv >> __AFS_PAGE_PRIV_SHIFT) & __AFS_PAGE_PRIV_MASK;
	unsigned long x = (priv >> __AFS_FOLIO_PRIV_SHIFT) & __AFS_FOLIO_PRIV_MASK;

	/* The upper bound is immediately beyond the region */
	return (x + 1) << afs_page_dirty_resolution(page);
	return (x + 1) << afs_folio_dirty_resolution(folio);
}

static inline unsigned long afs_page_dirty(struct page *page, size_t from, size_t to)
static inline unsigned long afs_folio_dirty(struct folio *folio, size_t from, size_t to)
{
	unsigned int res = afs_page_dirty_resolution(page);
	unsigned int res = afs_folio_dirty_resolution(folio);
	from >>= res;
	to = (to - 1) >> res;
	return (to << __AFS_PAGE_PRIV_SHIFT) | from;
	return (to << __AFS_FOLIO_PRIV_SHIFT) | from;
}

static inline unsigned long afs_page_dirty_mmapped(unsigned long priv)
static inline unsigned long afs_folio_dirty_mmapped(unsigned long priv)
{
	return priv | __AFS_PAGE_PRIV_MMAPPED;
	return priv | __AFS_FOLIO_PRIV_MMAPPED;
}

static inline bool afs_is_page_dirty_mmapped(unsigned long priv)
static inline bool afs_is_folio_dirty_mmapped(unsigned long priv)
{
	return priv & __AFS_PAGE_PRIV_MMAPPED;
	return priv & __AFS_FOLIO_PRIV_MMAPPED;
}

#include <trace/events/afs.h>
+167 −180

File changed.

Preview size limit exceeded, changes collapsed.

Loading