Skip to content
Permalink
Browse files
netfs, afs, ceph: Use folios
Convert the netfs helper library and the afs filesystem to use folios.

NOTE: This patch will also need to alter the ceph filesystem, but as that's
not been done that yet, ceph will fail to build.

The patch makes two alterations to the mm headers:

 (1) Fix a bug in readahead_folio() where a NULL return from
     __readahead_folio() will cause folio_put() to oops.

 (2) Add folio_change_private() to change the private data on the folio
     without adjusting the page refcount or changing the flag.  This
     assumes folio_attach_private() was already called.

Notes:

 (*) Should I be using page_mapping() or page_file_mapping()?

 (*) Can page_endio() be split into two separate functions, one for read
     and one for write?  If seems a waste of time to conditionally switch
     between two different branches.

 (*) Is there a better way to implement afs_kill_pages() and
     afs_redirty_pages()?  I was previously using find_get_pages_contig()
     into a pagevec, but that doesn't look like it'll work with folios, so
     I'm now calling filemap_get_folio() a lot more - not that it matters
     so much, as these are failure paths.

     Also, should these be moved into generic code?

 (*) Can ->page_mkwrite() see which subpage of a folio got hit?

 (*) afs_launder_page() has a bug in it that needs a separate patch.

 (*) readahead_folio() puts the page whereas readahead_page() does not.

 (*) __filemap_get_folio() should be used instead of
     grab_cache_page_write_begin()?  What should be done if xa_is_value()
     returns true on the value returned by that?

With these changes, afs passes -g quick xfstests.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: linux-afs@lists.infradead.org
cc: ceph-devel@vger.kernel.org
cc: linux-cachefs@redhat.com
  • Loading branch information
dhowells authored and intel-lab-lkp committed Aug 11, 2021
1 parent 8ca403f commit a665390ce411c517db3f70ae59cdaa874cb914ba
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 327 deletions.
@@ -308,23 +308,24 @@ static void afs_req_issue_op(struct netfs_read_subrequest *subreq)

static int afs_symlink_readpage(struct page *page)
{
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
struct afs_vnode *vnode = AFS_FS_I(page_mapping(page)->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_file_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);
page_endio(&folio->page, false, ret);
return ret;
}

@@ -348,7 +349,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));

@@ -371,10 +372,12 @@ const struct netfs_read_request_ops afs_req_ops = {

static int afs_readpage(struct file *file, struct page *page)
{
struct folio *folio = page_folio(page);

if (!file)
return afs_symlink_readpage(page);

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

static void afs_readahead(struct readahead_control *ractl)
@@ -386,29 +389,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_mapping(folio)->host);
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 */
@@ -426,17 +429,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);
}

/*
@@ -447,14 +450,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("");
}

@@ -464,30 +469,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_mapping(folio)->host);

_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;
}

/*
@@ -867,59 +867,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>

0 comments on commit a665390

Please sign in to comment.