fuse: allow splice to move pages
authorMiklos Szeredi <mszeredi@suse.cz>
Tue, 25 May 2010 13:06:07 +0000 (15:06 +0200)
committerMiklos Szeredi <mszeredi@suse.cz>
Tue, 25 May 2010 13:06:07 +0000 (15:06 +0200)
commitce534fb052928ce556639d7ecf01cbf4e01321e1
treec09f7c592a41d635d7f2f54fc7fe10594f332b69
parenta52116aba5b3eed0ee41f70b794cc1937acd5cb8
fuse: allow splice to move pages

When splicing buffers to the fuse device with SPLICE_F_MOVE, try to
move pages from the pipe buffer into the page cache.  This allows
populating the fuse filesystem's cache without ever touching the page
contents, i.e. zero copy read capability.

The following steps are performed when trying to move a page into the
page cache:

 - buf->ops->confirm() to make sure the new page is uptodate
 - buf->ops->steal() to try to remove the new page from it's previous place
 - remove_from_page_cache() on the old page
 - add_to_page_cache_locked() on the new page

If any of the above steps fail (non fatally) then the code falls back
to copying the page.  In particular ->steal() will fail if there are
external references (other than the page cache and the pipe buffer) to
the page.

Also since the remove_from_page_cache() + add_to_page_cache_locked()
are non-atomic it is possible that the page cache is repopulated in
between the two and add_to_page_cache_locked() will fail.  This could
be fixed by creating a new atomic replace_page_cache_page() function.

fuse_readpages_end() needed to be reworked so it works even if
page->mapping is NULL for some or all pages which can happen if the
add_to_page_cache_locked() failed.

A number of sanity checks were added to make sure the stolen pages
don't have weird flags set, etc...  These could be moved into generic
splice/steal code.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
fs/fuse/dev.c
fs/fuse/file.c
fs/fuse/fuse_i.h