* How can we share page cache pages for reflinked files?
@ 2017-08-10 4:28 Dave Chinner
2017-08-10 5:57 ` Kirill A. Shutemov
2017-08-10 16:11 ` Matthew Wilcox
0 siblings, 2 replies; 16+ messages in thread
From: Dave Chinner @ 2017-08-10 4:28 UTC (permalink / raw)
To: linux-fsdevel; +Cc: linux-mm
Hi folks,
I've recently been looking into what is involved in sharing page
cache pages for shared extents in a filesystem. That is, create a
file, reflink it so there's two files but only one copy of the data
on disk, then read both files. Right now, we get two copies of the
data in the page cache - one in each inode mapping tree.
If we scale this up to a container host which is using reflink trees
it's shared root images, there might be hundreds of copies of the
same data held in cache (i.e. one page per container). Given that
the filesystem knows that the underlying data extent is shared when
we go to read it, it's relatively easy to add mechanisms to the
filesystem to return the same page for all attempts to read the
from a shared extent from all inodes that share it.
However, the problem I'm getting stuck on is that the page cache
itself can't handle inserting a single page into multiple page cache
mapping trees. i.e. The page has a single pointer to the mapping
address space, and the mapping has a single pointer back to the
owner inode. As such, a cached page has a 1:1 mapping to it's host
inode and this structure seems to be assumed rather widely through
the code.
The problem is somewhat limited by the fact that only clean,
read-only pages would be shared, and the attempt to write/dirty
a shared page in a mapping would trigger a COW operation in the
filesystem which would invalidate that inode's shared page and
replace it with a new, inode-private page that could be written to.
This still requires us to be able to find the right inode from the
shared page context to run the COW operation. Luckily, the IO path
already has an inode pointer, and the page fault path provides us
with the inode via file_inode(vmf->vma->vm_file) so we don't
actually need page->mapping->host in these paths.
Along these lines I've thought about using a "shared mapping" that
is associated with the filesystem rather than a specific inode (like
a bdev mapping), but that's no good because if page->mapping !=
inode->i_mapping the page is consider to have been invalidated and
should be considered invalid.
Further - a page has a single, fixed index into the mapping tree
(i.e. page->index), so this prevents arbitrary page sharing across
inodes (the "deduplication triggered shared extent" case). And we
can't really get rid of the page index, because that's how the page
finds itself in a mapping tree.
This leads me to think about crazy schemes like allocating a
"referring struct page" that is allocated for every reference to a
shared cache page and chain them all to the real struct page sorta
like we do for compound pages. That would give us a unique struct
page for each mapping tree and solve many of the issues, but I'm not
sure how viable such a concept would be.
I'm sure there's more issues than I've outlined here, but I haven't
gone deeper than this because I've got to solve the one to many
problem first. I don't know if anyone has looked at this in any
detail, so I don't know what ideas, patches, crazy schemes, etc
might already exist out there. Right now I'm just looking for
information to narrow down what I need to look at - finding what
rabbit holes have already been explored and what dragons are already
known about would help an awful lot right now.
Anyone?
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 4:28 How can we share page cache pages for reflinked files? Dave Chinner
@ 2017-08-10 5:57 ` Kirill A. Shutemov
2017-08-10 9:01 ` Dave Chinner
2017-08-10 16:11 ` Matthew Wilcox
1 sibling, 1 reply; 16+ messages in thread
From: Kirill A. Shutemov @ 2017-08-10 5:57 UTC (permalink / raw)
To: Dave Chinner; +Cc: linux-fsdevel, linux-mm
On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> Hi folks,
>
> I've recently been looking into what is involved in sharing page
> cache pages for shared extents in a filesystem. That is, create a
> file, reflink it so there's two files but only one copy of the data
> on disk, then read both files. Right now, we get two copies of the
> data in the page cache - one in each inode mapping tree.
>
> If we scale this up to a container host which is using reflink trees
> it's shared root images, there might be hundreds of copies of the
> same data held in cache (i.e. one page per container). Given that
> the filesystem knows that the underlying data extent is shared when
> we go to read it, it's relatively easy to add mechanisms to the
> filesystem to return the same page for all attempts to read the
> from a shared extent from all inodes that share it.
>
> However, the problem I'm getting stuck on is that the page cache
> itself can't handle inserting a single page into multiple page cache
> mapping trees. i.e. The page has a single pointer to the mapping
> address space, and the mapping has a single pointer back to the
> owner inode. As such, a cached page has a 1:1 mapping to it's host
> inode and this structure seems to be assumed rather widely through
> the code.
I think to solve the problem with page->mapping we need something similar
to what we have for anon rmap[1]. In this case we would be able to keep
the same page in page cache for multiple inodes.
The long term benefit for this is that we might be able to unify a lot of
code for anon and file code paths in mm, making anon memory a special case
of file mapping.
The downside is that anon rmap is rather complicated. I have to re-read
the article everytime I deal with anon rmap to remind myself how it works.
[1] https://lwn.net/Articles/383162/
--
Kirill A. Shutemov
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 5:57 ` Kirill A. Shutemov
@ 2017-08-10 9:01 ` Dave Chinner
2017-08-10 13:31 ` Kirill A. Shutemov
0 siblings, 1 reply; 16+ messages in thread
From: Dave Chinner @ 2017-08-10 9:01 UTC (permalink / raw)
To: Kirill A. Shutemov; +Cc: linux-fsdevel, linux-mm
On Thu, Aug 10, 2017 at 08:57:37AM +0300, Kirill A. Shutemov wrote:
> On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > Hi folks,
> >
> > I've recently been looking into what is involved in sharing page
> > cache pages for shared extents in a filesystem. That is, create a
> > file, reflink it so there's two files but only one copy of the data
> > on disk, then read both files. Right now, we get two copies of the
> > data in the page cache - one in each inode mapping tree.
> >
> > If we scale this up to a container host which is using reflink trees
> > it's shared root images, there might be hundreds of copies of the
> > same data held in cache (i.e. one page per container). Given that
> > the filesystem knows that the underlying data extent is shared when
> > we go to read it, it's relatively easy to add mechanisms to the
> > filesystem to return the same page for all attempts to read the
> > from a shared extent from all inodes that share it.
> >
> > However, the problem I'm getting stuck on is that the page cache
> > itself can't handle inserting a single page into multiple page cache
> > mapping trees. i.e. The page has a single pointer to the mapping
> > address space, and the mapping has a single pointer back to the
> > owner inode. As such, a cached page has a 1:1 mapping to it's host
> > inode and this structure seems to be assumed rather widely through
> > the code.
>
> I think to solve the problem with page->mapping we need something similar
> to what we have for anon rmap[1]. In this case we would be able to keep
> the same page in page cache for multiple inodes.
Being unfamiliar with the anon rmap code, I'm struggling to see the
need for that much complexity here. The AVC abstraction solves a
scalability problem that, to me, doesn't exist for tracking multiple
mapping tree pointers for a page. i.e. I don't see where a list
traversal is necessary in the shared page -> mapping tree resolution
for page cache sharing.
I've been thinking of something simpler along the lines of a dynamic
struct page objects w/ special page flags as an object that allows
us to keep different mapping tree entries for the same physical
page. Seems like this would work for read-only sharing, but perhaps
I'm just blind and I'm missing something I shouldn't be?
> The long term benefit for this is that we might be able to unify a lot of
> code for anon and file code paths in mm, making anon memory a special case
> of file mapping.
>
> The downside is that anon rmap is rather complicated. I have to re-read
> the article everytime I deal with anon rmap to remind myself how it works.
Yeah, that's a problem - if you have trouble with it, I've got no
hope.... :/
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 9:01 ` Dave Chinner
@ 2017-08-10 13:31 ` Kirill A. Shutemov
2017-08-11 3:59 ` Dave Chinner
0 siblings, 1 reply; 16+ messages in thread
From: Kirill A. Shutemov @ 2017-08-10 13:31 UTC (permalink / raw)
To: Dave Chinner; +Cc: linux-fsdevel, linux-mm, Rik van Riel
On Thu, Aug 10, 2017 at 07:01:33PM +1000, Dave Chinner wrote:
> On Thu, Aug 10, 2017 at 08:57:37AM +0300, Kirill A. Shutemov wrote:
> > On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > > Hi folks,
> > >
> > > I've recently been looking into what is involved in sharing page
> > > cache pages for shared extents in a filesystem. That is, create a
> > > file, reflink it so there's two files but only one copy of the data
> > > on disk, then read both files. Right now, we get two copies of the
> > > data in the page cache - one in each inode mapping tree.
> > >
> > > If we scale this up to a container host which is using reflink trees
> > > it's shared root images, there might be hundreds of copies of the
> > > same data held in cache (i.e. one page per container). Given that
> > > the filesystem knows that the underlying data extent is shared when
> > > we go to read it, it's relatively easy to add mechanisms to the
> > > filesystem to return the same page for all attempts to read the
> > > from a shared extent from all inodes that share it.
> > >
> > > However, the problem I'm getting stuck on is that the page cache
> > > itself can't handle inserting a single page into multiple page cache
> > > mapping trees. i.e. The page has a single pointer to the mapping
> > > address space, and the mapping has a single pointer back to the
> > > owner inode. As such, a cached page has a 1:1 mapping to it's host
> > > inode and this structure seems to be assumed rather widely through
> > > the code.
> >
> > I think to solve the problem with page->mapping we need something similar
> > to what we have for anon rmap[1]. In this case we would be able to keep
> > the same page in page cache for multiple inodes.
>
> Being unfamiliar with the anon rmap code, I'm struggling to see the
> need for that much complexity here. The AVC abstraction solves a
> scalability problem that, to me, doesn't exist for tracking multiple
> mapping tree pointers for a page. i.e. I don't see where a list
> traversal is necessary in the shared page -> mapping tree resolution
> for page cache sharing.
[ Cc: Rik ]
The reflink interface has potential to construct a tree of dependencies
between reflinked files similar in complexity to tree of forks (and CoWed
anon mappings) that lead to current anon rmap design.
But it's harder to get there accidentally. :)
> I've been thinking of something simpler along the lines of a dynamic
> struct page objects w/ special page flags as an object that allows
> us to keep different mapping tree entries for the same physical
> page. Seems like this would work for read-only sharing, but perhaps
> I'm just blind and I'm missing something I shouldn't be?
Naive approach would be just to put all connected through reflink mappings
on the same linked list. page->mapping can point to any of them in this
case. To check that the page actually belong to the mapping we would need
to look into radix-tree.
Something like this we had before current anon rmap design, but with
checking page tables instead of radix-tree as primary reference.
> > The long term benefit for this is that we might be able to unify a lot of
> > code for anon and file code paths in mm, making anon memory a special case
> > of file mapping.
> >
> > The downside is that anon rmap is rather complicated. I have to re-read
> > the article everytime I deal with anon rmap to remind myself how it works.
>
> Yeah, that's a problem - if you have trouble with it, I've got no
> hope.... :/
--
Kirill A. Shutemov
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 4:28 How can we share page cache pages for reflinked files? Dave Chinner
2017-08-10 5:57 ` Kirill A. Shutemov
@ 2017-08-10 16:11 ` Matthew Wilcox
2017-08-10 19:17 ` Vivek Goyal
2017-08-11 4:25 ` Dave Chinner
1 sibling, 2 replies; 16+ messages in thread
From: Matthew Wilcox @ 2017-08-10 16:11 UTC (permalink / raw)
To: Dave Chinner; +Cc: linux-fsdevel, linux-mm
On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> I've recently been looking into what is involved in sharing page
> cache pages for shared extents in a filesystem. That is, create a
> file, reflink it so there's two files but only one copy of the data
> on disk, then read both files. Right now, we get two copies of the
> data in the page cache - one in each inode mapping tree.
Yep. We had a brief discussion of this at LSFMM (as you know, since you
commented on the discussion): https://lwn.net/Articles/717950/
> If we scale this up to a container host which is using reflink trees
> it's shared root images, there might be hundreds of copies of the
> same data held in cache (i.e. one page per container). Given that
> the filesystem knows that the underlying data extent is shared when
> we go to read it, it's relatively easy to add mechanisms to the
> filesystem to return the same page for all attempts to read the
> from a shared extent from all inodes that share it.
I agree the problem exists. Should we try to fix this problem, or
should we steer people towards solutions which don't have this problem?
The solutions I've been seeing use COW block devices instead of COW
filesystems, and DAX to share the common pages between the host and
each guest.
> This leads me to think about crazy schemes like allocating a
> "referring struct page" that is allocated for every reference to a
> shared cache page and chain them all to the real struct page sorta
> like we do for compound pages. That would give us a unique struct
> page for each mapping tree and solve many of the issues, but I'm not
> sure how viable such a concept would be.
That's the solution I'd recommend looking into deeper. We've also talked
about creating referring struct pages to support block size > page size.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 16:11 ` Matthew Wilcox
@ 2017-08-10 19:17 ` Vivek Goyal
2017-08-10 21:20 ` Matthew Wilcox
2017-08-11 4:25 ` Dave Chinner
1 sibling, 1 reply; 16+ messages in thread
From: Vivek Goyal @ 2017-08-10 19:17 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: Dave Chinner, linux-fsdevel, linux-mm
On Thu, Aug 10, 2017 at 09:11:59AM -0700, Matthew Wilcox wrote:
> On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > I've recently been looking into what is involved in sharing page
> > cache pages for shared extents in a filesystem. That is, create a
> > file, reflink it so there's two files but only one copy of the data
> > on disk, then read both files. Right now, we get two copies of the
> > data in the page cache - one in each inode mapping tree.
>
> Yep. We had a brief discussion of this at LSFMM (as you know, since you
> commented on the discussion): https://lwn.net/Articles/717950/
>
> > If we scale this up to a container host which is using reflink trees
> > it's shared root images, there might be hundreds of copies of the
> > same data held in cache (i.e. one page per container). Given that
> > the filesystem knows that the underlying data extent is shared when
> > we go to read it, it's relatively easy to add mechanisms to the
> > filesystem to return the same page for all attempts to read the
> > from a shared extent from all inodes that share it.
>
> I agree the problem exists. Should we try to fix this problem, or
> should we steer people towards solutions which don't have this problem?
> The solutions I've been seeing use COW block devices instead of COW
> filesystems, and DAX to share the common pages between the host and
> each guest.
Hi Matthew,
This is in the context of clear containers? It would be good to have
a solution for those who are not launching virt guests.
overlayfs helps mitigate this page cache sharing issue but xfs reflink
and dm thin pool continue to face this issue.
Vivek
>
> > This leads me to think about crazy schemes like allocating a
> > "referring struct page" that is allocated for every reference to a
> > shared cache page and chain them all to the real struct page sorta
> > like we do for compound pages. That would give us a unique struct
> > page for each mapping tree and solve many of the issues, but I'm not
> > sure how viable such a concept would be.
>
> That's the solution I'd recommend looking into deeper. We've also talked
> about creating referring struct pages to support block size > page size.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 19:17 ` Vivek Goyal
@ 2017-08-10 21:20 ` Matthew Wilcox
0 siblings, 0 replies; 16+ messages in thread
From: Matthew Wilcox @ 2017-08-10 21:20 UTC (permalink / raw)
To: Vivek Goyal; +Cc: Dave Chinner, linux-fsdevel, linux-mm
On Thu, Aug 10, 2017 at 03:17:46PM -0400, Vivek Goyal wrote:
> On Thu, Aug 10, 2017 at 09:11:59AM -0700, Matthew Wilcox wrote:
> > On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > > If we scale this up to a container host which is using reflink trees
> > > it's shared root images, there might be hundreds of copies of the
> > > same data held in cache (i.e. one page per container). Given that
> > > the filesystem knows that the underlying data extent is shared when
> > > we go to read it, it's relatively easy to add mechanisms to the
> > > filesystem to return the same page for all attempts to read the
> > > from a shared extent from all inodes that share it.
> >
> > I agree the problem exists. Should we try to fix this problem, or
> > should we steer people towards solutions which don't have this problem?
> > The solutions I've been seeing use COW block devices instead of COW
> > filesystems, and DAX to share the common pages between the host and
> > each guest.
>
> Hi Matthew,
>
> This is in the context of clear containers? It would be good to have
> a solution for those who are not launching virt guests.
>
> overlayfs helps mitigate this page cache sharing issue but xfs reflink
> and dm thin pool continue to face this issue.
Right, this is with clear containers. But there's no reason it couldn't
be used with other virtualisation solutions.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 13:31 ` Kirill A. Shutemov
@ 2017-08-11 3:59 ` Dave Chinner
2017-08-11 12:57 ` Kirill A. Shutemov
0 siblings, 1 reply; 16+ messages in thread
From: Dave Chinner @ 2017-08-11 3:59 UTC (permalink / raw)
To: Kirill A. Shutemov; +Cc: linux-fsdevel, linux-mm, Rik van Riel
On Thu, Aug 10, 2017 at 04:31:18PM +0300, Kirill A. Shutemov wrote:
> On Thu, Aug 10, 2017 at 07:01:33PM +1000, Dave Chinner wrote:
> > On Thu, Aug 10, 2017 at 08:57:37AM +0300, Kirill A. Shutemov wrote:
> > > On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > > > Hi folks,
> > > >
> > > > I've recently been looking into what is involved in sharing page
> > > > cache pages for shared extents in a filesystem. That is, create a
> > > > file, reflink it so there's two files but only one copy of the data
> > > > on disk, then read both files. Right now, we get two copies of the
> > > > data in the page cache - one in each inode mapping tree.
> > > >
> > > > If we scale this up to a container host which is using reflink trees
> > > > it's shared root images, there might be hundreds of copies of the
> > > > same data held in cache (i.e. one page per container). Given that
> > > > the filesystem knows that the underlying data extent is shared when
> > > > we go to read it, it's relatively easy to add mechanisms to the
> > > > filesystem to return the same page for all attempts to read the
> > > > from a shared extent from all inodes that share it.
> > > >
> > > > However, the problem I'm getting stuck on is that the page cache
> > > > itself can't handle inserting a single page into multiple page cache
> > > > mapping trees. i.e. The page has a single pointer to the mapping
> > > > address space, and the mapping has a single pointer back to the
> > > > owner inode. As such, a cached page has a 1:1 mapping to it's host
> > > > inode and this structure seems to be assumed rather widely through
> > > > the code.
> > >
> > > I think to solve the problem with page->mapping we need something similar
> > > to what we have for anon rmap[1]. In this case we would be able to keep
> > > the same page in page cache for multiple inodes.
> >
> > Being unfamiliar with the anon rmap code, I'm struggling to see the
> > need for that much complexity here. The AVC abstraction solves a
> > scalability problem that, to me, doesn't exist for tracking multiple
> > mapping tree pointers for a page. i.e. I don't see where a list
> > traversal is necessary in the shared page -> mapping tree resolution
> > for page cache sharing.
>
> [ Cc: Rik ]
>
> The reflink interface has potential to construct a tree of dependencies
> between reflinked files similar in complexity to tree of forks (and CoWed
> anon mappings) that lead to current anon rmap design.
I'm too stupid to see the operation that would create the tree of
dependencies you are talking about. Can you outline how we get to
that situation?
AFAICT, the dependencies just don't exist because the reflink
operations don't duplicate the page cache into the new file. And
when we are doing a cache lookup, we are looking for a page with a matching *block address*,
not a specific mapping or index in the cache. The cached page could
be anywhere in the filesystem, it could even be on a different
block device and filesystem. Nothing we have in the page cache
indexes physical block addresses, so these lookups cannot be done
via the page cache.
Physical block index lookups, of course, is what buffer caches are
for. So essentially the process of sharing the pages cached on a
shared extent is this:
Cold cache:
1. lookup extent map
2. find IOMAP_SHARED is set on the extent
3. Look up iomap->blkno in buffer cache
a. Search for cached block
b. not found, instantiate, attach page to buffer
c. take ref to page, return struct page
4. insert struct page into page cache
5. do read IO into page.
Hot cache: Only step 3 changes:
3. Look up iomap->blkno in buffer cache
a. Search for cached block
b. Found, take ref to page,
c. return struct page
IOWs, we use a buffer cache to provide an inclusive global L2 cache
for pages cached over shared blocks within a filesystem LBA space.
This means there is no "needle in a haystack" search for matching
shared cached pages, nor is there complex dependency graph between
shared pages and mappings.
The only thing that makes this not work right now is that a
struct page can't be shared across mutliple mappings....
> But it's harder to get there accidentally. :)
>
> > I've been thinking of something simpler along the lines of a dynamic
> > struct page objects w/ special page flags as an object that allows
> > us to keep different mapping tree entries for the same physical
> > page. Seems like this would work for read-only sharing, but perhaps
> > I'm just blind and I'm missing something I shouldn't be?
>
> Naive approach would be just to put all connected through reflink mappings
> on the same linked list. page->mapping can point to any of them in this
> case. To check that the page actually belong to the mapping we would need
> to look into radix-tree.
There is so much code that assumes page->mapping points to the
mapping (and hence mapping tree) the page has been inserted into.
Indeed, this is how we check for racing with page
invalidation/reclaim after a lookup. i.e. once we've locked a page,
if page->mapping is different to the current mapping we have, then
the page is considered invalid and we shouldn't touch it. This
mechanism is the only thing that makes truncate work correctly in
many filesystems, so changing this is pretty much a non-starter.
Put simply, I'm trying to find a solution that doesn't start with
"break all the filesystems".... :/
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-10 16:11 ` Matthew Wilcox
2017-08-10 19:17 ` Vivek Goyal
@ 2017-08-11 4:25 ` Dave Chinner
2017-08-11 17:08 ` Matthew Wilcox
1 sibling, 1 reply; 16+ messages in thread
From: Dave Chinner @ 2017-08-11 4:25 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: linux-fsdevel, linux-mm
On Thu, Aug 10, 2017 at 09:11:59AM -0700, Matthew Wilcox wrote:
> On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > I've recently been looking into what is involved in sharing page
> > cache pages for shared extents in a filesystem. That is, create a
> > file, reflink it so there's two files but only one copy of the data
> > on disk, then read both files. Right now, we get two copies of the
> > data in the page cache - one in each inode mapping tree.
>
> Yep. We had a brief discussion of this at LSFMM (as you know, since you
> commented on the discussion): https://lwn.net/Articles/717950/
*nod*
> > If we scale this up to a container host which is using reflink trees
> > it's shared root images, there might be hundreds of copies of the
> > same data held in cache (i.e. one page per container). Given that
> > the filesystem knows that the underlying data extent is shared when
> > we go to read it, it's relatively easy to add mechanisms to the
> > filesystem to return the same page for all attempts to read the
> > from a shared extent from all inodes that share it.
>
> I agree the problem exists. Should we try to fix this problem, or
> should we steer people towards solutions which don't have this problem?
> The solutions I've been seeing use COW block devices instead of COW
> filesystems, and DAX to share the common pages between the host and
> each guest.
That's one possible solution for people using hardware
virutalisation, but not everyone is doing that. It also relies on
block devices, which rules out a whole bunch of interesting stuff we
can do with filesystems...
> > This leads me to think about crazy schemes like allocating a
> > "referring struct page" that is allocated for every reference to a
> > shared cache page and chain them all to the real struct page sorta
> > like we do for compound pages. That would give us a unique struct
> > page for each mapping tree and solve many of the issues, but I'm not
> > sure how viable such a concept would be.
>
> That's the solution I'd recommend looking into deeper.
OK, I'll dig deeper into that, try and understand what happens if we
put such things into the LRUs so reclaim can act on them.
> We've also talked
> about creating referring struct pages to support block size > page size.
Yup, that's a small extension of the infrastructure we need in XFS
for caching shared blocks. :)
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-11 3:59 ` Dave Chinner
@ 2017-08-11 12:57 ` Kirill A. Shutemov
0 siblings, 0 replies; 16+ messages in thread
From: Kirill A. Shutemov @ 2017-08-11 12:57 UTC (permalink / raw)
To: Dave Chinner; +Cc: linux-fsdevel, linux-mm, Rik van Riel
On Fri, Aug 11, 2017 at 01:59:22PM +1000, Dave Chinner wrote:
> On Thu, Aug 10, 2017 at 04:31:18PM +0300, Kirill A. Shutemov wrote:
> > On Thu, Aug 10, 2017 at 07:01:33PM +1000, Dave Chinner wrote:
> > > On Thu, Aug 10, 2017 at 08:57:37AM +0300, Kirill A. Shutemov wrote:
> > > > On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > > > > Hi folks,
> > > > >
> > > > > I've recently been looking into what is involved in sharing page
> > > > > cache pages for shared extents in a filesystem. That is, create a
> > > > > file, reflink it so there's two files but only one copy of the data
> > > > > on disk, then read both files. Right now, we get two copies of the
> > > > > data in the page cache - one in each inode mapping tree.
> > > > >
> > > > > If we scale this up to a container host which is using reflink trees
> > > > > it's shared root images, there might be hundreds of copies of the
> > > > > same data held in cache (i.e. one page per container). Given that
> > > > > the filesystem knows that the underlying data extent is shared when
> > > > > we go to read it, it's relatively easy to add mechanisms to the
> > > > > filesystem to return the same page for all attempts to read the
> > > > > from a shared extent from all inodes that share it.
> > > > >
> > > > > However, the problem I'm getting stuck on is that the page cache
> > > > > itself can't handle inserting a single page into multiple page cache
> > > > > mapping trees. i.e. The page has a single pointer to the mapping
> > > > > address space, and the mapping has a single pointer back to the
> > > > > owner inode. As such, a cached page has a 1:1 mapping to it's host
> > > > > inode and this structure seems to be assumed rather widely through
> > > > > the code.
> > > >
> > > > I think to solve the problem with page->mapping we need something similar
> > > > to what we have for anon rmap[1]. In this case we would be able to keep
> > > > the same page in page cache for multiple inodes.
> > >
> > > Being unfamiliar with the anon rmap code, I'm struggling to see the
> > > need for that much complexity here. The AVC abstraction solves a
> > > scalability problem that, to me, doesn't exist for tracking multiple
> > > mapping tree pointers for a page. i.e. I don't see where a list
> > > traversal is necessary in the shared page -> mapping tree resolution
> > > for page cache sharing.
> >
> > [ Cc: Rik ]
> >
> > The reflink interface has potential to construct a tree of dependencies
> > between reflinked files similar in complexity to tree of forks (and CoWed
> > anon mappings) that lead to current anon rmap design.
>
> I'm too stupid to see the operation that would create the tree of
> dependencies you are talking about. Can you outline how we get to
> that situation?
>
> AFAICT, the dependencies just don't exist because the reflink
> operations don't duplicate the page cache into the new file. And
> when we are doing a cache lookup, we are looking for a page with a matching *block address*,
> not a specific mapping or index in the cache. The cached page could
> be anywhere in the filesystem, it could even be on a different
> block device and filesystem. Nothing we have in the page cache
> indexes physical block addresses, so these lookups cannot be done
> via the page cache.
>
> Physical block index lookups, of course, is what buffer caches are
> for. So essentially the process of sharing the pages cached on a
> shared extent is this:
>
> Cold cache:
>
> 1. lookup extent map
> 2. find IOMAP_SHARED is set on the extent
> 3. Look up iomap->blkno in buffer cache
> a. Search for cached block
> b. not found, instantiate, attach page to buffer
> c. take ref to page, return struct page
> 4. insert struct page into page cache
> 5. do read IO into page.
>
> Hot cache: Only step 3 changes:
>
> 3. Look up iomap->blkno in buffer cache
> a. Search for cached block
> b. Found, take ref to page,
> c. return struct page
>
> IOWs, we use a buffer cache to provide an inclusive global L2 cache
> for pages cached over shared blocks within a filesystem LBA space.
> This means there is no "needle in a haystack" search for matching
> shared cached pages, nor is there complex dependency graph between
> shared pages and mappings.
>
> The only thing that makes this not work right now is that a
> struct page can't be shared across mutliple mappings....
>
> > But it's harder to get there accidentally. :)
> >
> > > I've been thinking of something simpler along the lines of a dynamic
> > > struct page objects w/ special page flags as an object that allows
> > > us to keep different mapping tree entries for the same physical
> > > page. Seems like this would work for read-only sharing, but perhaps
> > > I'm just blind and I'm missing something I shouldn't be?
> >
> > Naive approach would be just to put all connected through reflink mappings
> > on the same linked list. page->mapping can point to any of them in this
> > case. To check that the page actually belong to the mapping we would need
> > to look into radix-tree.
>
> There is so much code that assumes page->mapping points to the
> mapping (and hence mapping tree) the page has been inserted into.
> Indeed, this is how we check for racing with page
> invalidation/reclaim after a lookup. i.e. once we've locked a page,
> if page->mapping is different to the current mapping we have, then
> the page is considered invalid and we shouldn't touch it. This
> mechanism is the only thing that makes truncate work correctly in
> many filesystems, so changing this is pretty much a non-starter.
>
> Put simply, I'm trying to find a solution that doesn't start with
> "break all the filesystems".... :/
I don't think there's any.
Your idea with multiple (dynamic) struct pages per physical page is not
feasible on many levels:
- We still need a consistent view on page metadata: PG_lock,
page_count(), page_mapcount(), etc. I don't think it solvable with
multiple struct pages;
- We need to be able to find *all* mappings for the physical page:
consider page reclaim, which required removing the page from all
radix-trees that has the physical page.
Basically, all these dynamic struct pages need to be aware about its
siblings that represents the same physical page.
We don't have space for it.
- It doesn't scale well: 1000 reflinked files with 1000 pages each would
require 1M struct pages.
The only option I see is to redefine meaning of page->mapping. It needs to
point to a data structure that can be used to find all mappings where the
page might be cached.
With the look up process you've described above, I don't longer think
linking all mappings is an option. And a data structure per phys page
looks too wasteful to me.
I've run out of ideas for now. :-/
--
Kirill A. Shutemov
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-11 4:25 ` Dave Chinner
@ 2017-08-11 17:08 ` Matthew Wilcox
2017-08-11 18:04 ` Christoph Hellwig
2017-08-14 6:48 ` Dave Chinner
0 siblings, 2 replies; 16+ messages in thread
From: Matthew Wilcox @ 2017-08-11 17:08 UTC (permalink / raw)
To: Dave Chinner; +Cc: linux-fsdevel, linux-mm
On Fri, Aug 11, 2017 at 02:25:19PM +1000, Dave Chinner wrote:
> On Thu, Aug 10, 2017 at 09:11:59AM -0700, Matthew Wilcox wrote:
> > On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > > If we scale this up to a container host which is using reflink trees
> > > it's shared root images, there might be hundreds of copies of the
> > > same data held in cache (i.e. one page per container). Given that
> > > the filesystem knows that the underlying data extent is shared when
> > > we go to read it, it's relatively easy to add mechanisms to the
> > > filesystem to return the same page for all attempts to read the
> > > from a shared extent from all inodes that share it.
> >
> > I agree the problem exists. Should we try to fix this problem, or
> > should we steer people towards solutions which don't have this problem?
> > The solutions I've been seeing use COW block devices instead of COW
> > filesystems, and DAX to share the common pages between the host and
> > each guest.
>
> That's one possible solution for people using hardware
> virutalisation, but not everyone is doing that. It also relies on
> block devices, which rules out a whole bunch of interesting stuff we
> can do with filesystems...
Assuming there's something fun we can do with filesystems that's
interesting to this type of user, what do you think to this:
Create a block device (maybe it's a loop device, maybe it's dm-raid0)
which supports DAX and uses the page cache to cache the physical pages
of the block device it's fronting.
Use XFS+reflink+DAX on top of this loop device. Now there's only one
copy of each page in RAM.
We'd need to be able to shoot down all mapped pages when evicting pages
from the loop device's page cache, but we have the right data structures
in place for that; we just need to use them.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-11 17:08 ` Matthew Wilcox
@ 2017-08-11 18:04 ` Christoph Hellwig
2017-08-14 6:48 ` Dave Chinner
1 sibling, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2017-08-11 18:04 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: Dave Chinner, linux-fsdevel, linux-mm
On Fri, Aug 11, 2017 at 10:08:47AM -0700, Matthew Wilcox wrote:
> Assuming there's something fun we can do with filesystems that's
> interesting to this type of user, what do you think to this:
>
> Create a block device (maybe it's a loop device, maybe it's dm-raid0)
> which supports DAX and uses the page cache to cache the physical pages
> of the block device it's fronting.
Why not make every block device just support fake DAX and avoid the
additional layer?
Basically this would be going back to a file cache indexed by
physical blocks from our logically indexed page cache model. And
for a fs using heavy reflinks that's probably the right model in the
end.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-11 17:08 ` Matthew Wilcox
2017-08-11 18:04 ` Christoph Hellwig
@ 2017-08-14 6:48 ` Dave Chinner
2017-08-14 18:14 ` Christopher Lameter
1 sibling, 1 reply; 16+ messages in thread
From: Dave Chinner @ 2017-08-14 6:48 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: linux-fsdevel, linux-mm
On Fri, Aug 11, 2017 at 10:08:47AM -0700, Matthew Wilcox wrote:
> On Fri, Aug 11, 2017 at 02:25:19PM +1000, Dave Chinner wrote:
> > On Thu, Aug 10, 2017 at 09:11:59AM -0700, Matthew Wilcox wrote:
> > > On Thu, Aug 10, 2017 at 02:28:49PM +1000, Dave Chinner wrote:
> > > > If we scale this up to a container host which is using reflink trees
> > > > it's shared root images, there might be hundreds of copies of the
> > > > same data held in cache (i.e. one page per container). Given that
> > > > the filesystem knows that the underlying data extent is shared when
> > > > we go to read it, it's relatively easy to add mechanisms to the
> > > > filesystem to return the same page for all attempts to read the
> > > > from a shared extent from all inodes that share it.
> > >
> > > I agree the problem exists. Should we try to fix this problem, or
> > > should we steer people towards solutions which don't have this problem?
> > > The solutions I've been seeing use COW block devices instead of COW
> > > filesystems, and DAX to share the common pages between the host and
> > > each guest.
> >
> > That's one possible solution for people using hardware
> > virutalisation, but not everyone is doing that. It also relies on
> > block devices, which rules out a whole bunch of interesting stuff we
> > can do with filesystems...
>
> Assuming there's something fun we can do with filesystems that's
> interesting to this type of user, what do you think to this:
>
> Create a block device (maybe it's a loop device, maybe it's dm-raid0)
> which supports DAX and uses the page cache to cache the physical pages
> of the block device it's fronting.
/me shudders and runs away screaming
<puff, puff, gasp>
Ok, I'm far away enough now. :P
> Use XFS+reflink+DAX on top of this loop device. Now there's only one
> copy of each page in RAM.
Yes, I can see how that could work. Crazy, out of the box, abuses
DAX for non-DAX purposes and uses stuff we haven't enabled yet
because nobody has done the work to validate it. Full points for
creativity! :)
However, I don't think it's a viable solution.
First, now *everything* is cached in a single global mapping tree
and that's going to affect scalability and likely also the working
set tracking in the mapping tree (now global rather than per-file).
That, in turn, will affect reclaim behaviour and patterns. I'll come
back to that.
Second, direct IO is no longer direct - it would now by cached
and concurrency is limited by the block device page cache, not the
capability and queue depth of the underlying device.
Third, I have a concern that while the filesystem might present to
userspace as a DAX filesystem, it does not present userspace with
same semantics as direct access to CPU addressable non-volatile
storage. That seems, to me, like minefield we don't want to step into.
And, finally, i can't see how it would work for sharing between
cloned filesystem images and snapshots. e.g. you use reflink to
clone the filesystem images exported by loopback devices. Or
dm-thinp to clone devices - there's no way for share page cache
pages for blocks that are shared across different dm-thinp devices
in the same pool. (And no, turtles is not the answer here :)
> We'd need to be able to shoot down all mapped pages when evicting pages
> from the loop device's page cache, but we have the right data structures
> in place for that; we just need to use them.
Sure. My biggest concern is whether reclaim can easily determine the
difference between a heavily shared page and a single use page? We'd
want to make sure we don't do stupid things like reclaim widely
shared pages from libc before we reclaim a page that has be read
only once in one context.
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-14 6:48 ` Dave Chinner
@ 2017-08-14 18:14 ` Christopher Lameter
2017-08-14 21:09 ` Kirill A. Shutemov
0 siblings, 1 reply; 16+ messages in thread
From: Christopher Lameter @ 2017-08-14 18:14 UTC (permalink / raw)
To: Dave Chinner; +Cc: Matthew Wilcox, linux-fsdevel, linux-mm
On Mon, 14 Aug 2017, Dave Chinner wrote:
> > Use XFS+reflink+DAX on top of this loop device. Now there's only one
> > copy of each page in RAM.
>
> Yes, I can see how that could work. Crazy, out of the box, abuses
> DAX for non-DAX purposes and uses stuff we haven't enabled yet
> because nobody has done the work to validate it. Full points for
> creativity! :)
Another not so crazy solution is to break the 1-1 relation between page
structs and pages. We already have issues with huge pages where one struct
page may represent 2m of memmory using 512 or so page struct.
Therer are also constantly attempts to expand struct page.
So how about an m->n relationship? Any page (may it be 4k, 2m or 1G) has
one page struct for each mapping that it is a member of?
Maybe a the page state could consist of a base struct that describes
the page state and then 1..n pieces of mapping information? In the future
other state info could be added to the end if we allow dynamic sizing of
page structs.
This would also allow the inevitable creeping page struct bloat to get
completely out of control.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-14 18:14 ` Christopher Lameter
@ 2017-08-14 21:09 ` Kirill A. Shutemov
2017-08-15 15:11 ` Christopher Lameter
0 siblings, 1 reply; 16+ messages in thread
From: Kirill A. Shutemov @ 2017-08-14 21:09 UTC (permalink / raw)
To: Christopher Lameter; +Cc: Dave Chinner, Matthew Wilcox, linux-fsdevel, linux-mm
On Mon, Aug 14, 2017 at 01:14:57PM -0500, Christopher Lameter wrote:
> On Mon, 14 Aug 2017, Dave Chinner wrote:
>
> > > Use XFS+reflink+DAX on top of this loop device. Now there's only one
> > > copy of each page in RAM.
> >
> > Yes, I can see how that could work. Crazy, out of the box, abuses
> > DAX for non-DAX purposes and uses stuff we haven't enabled yet
> > because nobody has done the work to validate it. Full points for
> > creativity! :)
>
> Another not so crazy solution is to break the 1-1 relation between page
> structs and pages. We already have issues with huge pages where one struct
> page may represent 2m of memmory using 512 or so page struct.
>
> Therer are also constantly attempts to expand struct page.
>
> So how about an m->n relationship? Any page (may it be 4k, 2m or 1G) has
> one page struct for each mapping that it is a member of?
>
> Maybe a the page state could consist of a base struct that describes
> the page state and then 1..n pieces of mapping information? In the future
> other state info could be added to the end if we allow dynamic sizing of
> page structs.
>
> This would also allow the inevitable creeping page struct bloat to get
> completely out of control.
Nice wish list. Add pony. :)
Any attempt to replace struct page with something more complex will have
severe performance implications. I'll be glad proved otherwise.
--
Kirill A. Shutemov
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: How can we share page cache pages for reflinked files?
2017-08-14 21:09 ` Kirill A. Shutemov
@ 2017-08-15 15:11 ` Christopher Lameter
0 siblings, 0 replies; 16+ messages in thread
From: Christopher Lameter @ 2017-08-15 15:11 UTC (permalink / raw)
To: Kirill A. Shutemov; +Cc: Dave Chinner, Matthew Wilcox, linux-fsdevel, linux-mm
On Tue, 15 Aug 2017, Kirill A. Shutemov wrote:
> > This would also allow the inevitable creeping page struct bloat to get
> > completely out of control.
>
> Nice wish list. Add pony. :)
>
> Any attempt to replace struct page with something more complex will have
> severe performance implications. I'll be glad proved otherwise.
Do we care that much anymore? I have people inserting all sorts of runtime
checks into hotpaths in the name of security.....
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2017-08-15 15:11 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-08-10 4:28 How can we share page cache pages for reflinked files? Dave Chinner
2017-08-10 5:57 ` Kirill A. Shutemov
2017-08-10 9:01 ` Dave Chinner
2017-08-10 13:31 ` Kirill A. Shutemov
2017-08-11 3:59 ` Dave Chinner
2017-08-11 12:57 ` Kirill A. Shutemov
2017-08-10 16:11 ` Matthew Wilcox
2017-08-10 19:17 ` Vivek Goyal
2017-08-10 21:20 ` Matthew Wilcox
2017-08-11 4:25 ` Dave Chinner
2017-08-11 17:08 ` Matthew Wilcox
2017-08-11 18:04 ` Christoph Hellwig
2017-08-14 6:48 ` Dave Chinner
2017-08-14 18:14 ` Christopher Lameter
2017-08-14 21:09 ` Kirill A. Shutemov
2017-08-15 15:11 ` Christopher Lameter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).