Re: [PATCH v2 04/12] mm/filemap: Add mapping_seek_hole_data

From: Matthew Wilcox
Date: Tue Sep 29 2020 - 08:43:28 EST


On Tue, Sep 29, 2020 at 10:46:53AM +0200, Jan Kara wrote:
> On Mon 14-09-20 14:00:34, Matthew Wilcox (Oracle) wrote:
> > Rewrite shmem_seek_hole_data() and move it to filemap.c.
> >
> > + rcu_read_lock();
> > + while ((page = xas_find_get_entry(&xas, max, XA_PRESENT))) {
> > + loff_t pos = xas.xa_index * PAGE_SIZE;
>
> OK, but for ordinary filesystems this could be problematic because of
> exceptional entries?

For ordinary filesystems, I have this queued up on top:

http://git.infradead.org/users/willy/pagecache.git/commitdiff/02c740b215bab901f95a560759b3bd906648da08

which handles exceptional entries. It treats shadow/swap/DAX entries
the same -- there's definitely data there, it's just not in a struct
page right now.

> Also for shmem you've dropped the PageUptodate check which I'm not sure is
> safe?

That was unintentional. I did run xfstests against this patch (just did
it again ... it passes), so I suspect it doesn't create a !Uptodate page.
I'll see if I can enhance the existing xfstests to catch this case.

The patch I link to above also doesn't handle !Uptodate pages on shmem
filesystems the same way that the current code does. So ... on top of
this patch, I propose doing this:

@@ -2416,6 +2416,14 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_ite
r *iter)
}
EXPORT_SYMBOL(generic_file_read_iter);

+static inline loff_t page_seek_hole_data(struct page *page,
+ loff_t start, loff_t end, bool seek_data)
+{
+ if (xa_is_value(page) || PageUptodate(page))
+ return seek_data ? start : end;
+ return seek_data ? end : start;
+}
+
static inline
unsigned int seek_page_size(struct xa_state *xas, struct page *page)
{
@@ -2463,10 +2471,10 @@ loff_t mapping_seek_hole_data(struct address_space *mapping, loff_t start,
start = pos;
}

- if (seek_data)
+ pos += seek_page_size(&xas, page);
+ start = page_seek_hole_data(page, start, pos, seek_data);
+ if (start < pos)
goto unlock;
-
- start = pos + seek_page_size(&xas, page);
}
rcu_read_unlock();


... and then rebasing the other patch on top of this works out nicely.

Here's the result:
http://git.infradead.org/users/willy/pagecache.git/commitdiff/9eb3f496b7cdcdcae83026e861e148f46921c367
http://git.infradead.org/users/willy/pagecache.git/commitdiff/7d93274088f0872d849a906d783dc260bee106b9