ceph: adapt ceph to the fscache rewrite
Subsystems (2)
cover ceph: adapt ceph to the fscache rewrite
This is a follow-on set for David Howells' recent patchset to rewrite the fscache and cachefiles infrastructure. This re-enables fscache read support in the ceph driver, and also adds support for writing to the cache as well.
What's the best way to handle these, going forward? David, would it be easier for you to carry these in your tree along with the rest of your series?
Jeff Layton (2):
ceph: conversion to new fscache API
ceph: add fscache writeback support
fs/ceph/Kconfig | 2 +- fs/ceph/addr.c | 99 +++++++++++++++++++------ fs/ceph/cache.c | 188 ++++++++++++++++++++---------------------------- fs/ceph/cache.h | 98 +++++++++++++++++-------- fs/ceph/caps.c | 3 +- fs/ceph/file.c | 13 +++- fs/ceph/inode.c | 22 ++++-- fs/ceph/super.c | 10 +-- fs/ceph/super.h | 2 +- 9 files changed, 255 insertions(+), 182 deletions(-)
Signature · 1 line
2.33.1
PATCH 1/2 ceph: conversion to new fscache API
Now that the fscache API has been reworked and simplified, change ceph over to use it.
With the old API, we would only instantiate a cookie when the file was open for reads. Change it to instantiate the cookie when the inode is instantiated and call use/unuse when the file is opened/closed.
Also, ensure we resize the cached data on truncates, and invalidate the cache in response to the appropriate events. This will allow us to plumb in write support later.
--- fs/ceph/Kconfig | 2 +- fs/ceph/addr.c | 32 +++++---- fs/ceph/cache.c | 188 ++++++++++++++++++++---------------------------- fs/ceph/cache.h | 98 +++++++++++++++++-------- fs/ceph/caps.c | 3 +- fs/ceph/file.c | 13 +++- fs/ceph/inode.c | 22 ++++-- fs/ceph/super.c | 10 +-- fs/ceph/super.h | 2 +- 9 files changed, 196 insertions(+), 174 deletions(-)
fs/ceph/Kconfig +1 -1
@@ -21,7 +21,7 @@ config CEPH_FS if CEPH_FS config CEPH_FSCACHE bool "Enable Ceph client caching support" - depends on CEPH_FS=m && FSCACHE_OLD_API || CEPH_FS=y && FSCACHE_OLD_API=y + depends on CEPH_FS=m && FSCACHE || CEPH_FS=y && FSCACHE=y help Choose Y here to enable persistent, read-only local caching support for Ceph clients using FS-Cache
fs/ceph/addr.c +19 -13
@@ -126,7 +126,7 @@ static int ceph_set_page_dirty(struct page *page)BUG_ON (PagePrivate (page));attach_page_private (page, snapc); -return __set_page_dirty_nobuffers (page); +return ceph_fscache_set_page_dirty (page); }/*
@@ -141,8 +141,6 @@ static void ceph_invalidatepage(struct page *page, unsigned int offset,struct ceph_inode_info *ci;struct ceph_snap_context *snapc; -wait_on_page_fscache (page); - inode = page->mapping->host; ci =ceph_inode (inode);
@@ -153,28 +151,36 @@ static void ceph_invalidatepage(struct page *page, unsigned int offset, }WARN_ON (!PageLocked (page)); -if (!PagePrivate (page)) -return ; +if (PagePrivate (page)) { +dout ("%p invalidatepage %p idx %lu full dirty page\n" , + inode, page, page->index); -dout ("%p invalidatepage %p idx %lu full dirty page\n" , - inode, page, page->index); + snapc =detach_page_private (page); +ceph_put_wrbuffer_cap_refs (ci,1 , snapc); +ceph_put_snap_context (snapc); + } - snapc =detach_page_private (page); -ceph_put_wrbuffer_cap_refs (ci,1 , snapc); -ceph_put_snap_context (snapc); +wait_on_page_fscache (page); }static int ceph_releasepage (struct page *page,gfp_t gfp) { -dout ("%p releasepage %p idx %lu (%sdirty)\n" , page->mapping->host, - page, page->index,PageDirty (page) ?"" :"not " ); +struct inode *inode = page->mapping->host; + +dout ("%llx:%llx releasepage %p idx %lu (%sdirty)\n" , +ceph_vinop (inode), page, + page->index,PageDirty (page) ?"" :"not " ); + +if (PagePrivate (page)) +return 0 ;if (PageFsCache (page)) {if (!(gfp & __GFP_DIRECT_RECLAIM) || !(gfp & __GFP_FS))return 0 ;wait_on_page_fscache (page); } -return !PagePrivate (page); +ceph_fscache_note_page_release (inode); +return 1 ; }static void ceph_netfs_expand_readahead (struct netfs_read_request *rreq)
fs/ceph/cache.c +77 -111
242 lines not on this page · raw message
fs/ceph/cache.h +69 -29
159 lines not on this page · raw message
fs/ceph/caps.c +2 -1
@@ -1857,7 +1857,7 @@ static int try_nonblocking_invalidate(struct inode *inode)u32 invalidating_gen = ci->i_rdcache_gen;spin_unlock (&ci->i_ceph_lock); -ceph_fscache_invalidate (inode); +ceph_fscache_invalidate (inode, false);invalidate_mapping_pages (&inode->i_data,0 ,-1 );spin_lock (&ci->i_ceph_lock);
@@ -2389,6 +2389,7 @@ int ceph_write_inode(struct inode *inode, struct writeback_control *wbc)int wait = (wbc->sync_mode ==WB_SYNC_ALL && !wbc->for_sync);dout ("write_inode %p wait=%d\n" , inode, wait); +ceph_fscache_unpin_writeback (inode, wbc);if (wait) { dirty =try_flush_caps (inode, &flush_tid);if (dirty)
fs/ceph/file.c +10 -3
@@ -248,8 +248,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)switch (inode->i_mode &S_IFMT ) {case S_IFREG : -ceph_fscache_register_inode_cookie (inode); -ceph_fscache_file_set_cookie (inode, file); +ceph_fscache_use_cookie (inode, file->f_mode &FMODE_WRITE ); fallthrough;case S_IFDIR : ret = ceph_init_file_info(inode, file, fmode,
@@ -810,6 +809,7 @@ int ceph_release(struct inode *inode, struct file *file)dout ("release inode %p regular file %p\n" , inode, file);WARN_ON (!list_empty (&fi->rw_contexts)); +ceph_fscache_unuse_cookie (inode, file->f_mode &FMODE_WRITE );ceph_put_fmode (ci, fi->fmode,1 );kmem_cache_free (ceph_file_cachep, fi);
@@ -1206,7 +1206,11 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, snapc, snapc ? snapc->seq : 0);if (write) { -int ret2 =invalidate_inode_pages2_range (inode->i_mapping, +int ret2; + +ceph_fscache_invalidate (inode, true); + + ret2 =invalidate_inode_pages2_range (inode->i_mapping, pos >>PAGE_SHIFT , (pos + count -1 ) >>PAGE_SHIFT );if (ret2 <0 )
@@ -1417,6 +1421,7 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos,if (ret <0 )return ret; +ceph_fscache_invalidate (inode, false); ret =invalidate_inode_pages2_range (inode->i_mapping, pos >>PAGE_SHIFT , (pos + count -1 ) >>PAGE_SHIFT );
@@ -2101,6 +2106,7 @@ static long ceph_fallocate(struct file *file, int mode, goto unlock;filemap_invalidate_lock (inode->i_mapping); +ceph_fscache_invalidate (inode, false);ceph_zero_pagecache_range (inode, offset, length); ret =ceph_zero_objects (inode, offset, length);
@@ -2425,6 +2431,7 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off, goto out_caps;/* Drop dst file cached pages */ +ceph_fscache_invalidate (dst_inode, false); ret =invalidate_inode_pages2_range (dst_inode->i_mapping, dst_off >>PAGE_SHIFT , (dst_off + len) >>PAGE_SHIFT );
fs/ceph/inode.c +16 -6
@@ -565,6 +565,8 @@ void ceph_evict_inode(struct inode *inode)percpu_counter_dec (&mdsc->metric.total_inodes);truncate_inode_pages_final (&inode->i_data); +if (inode->i_state &I_PINNING_FSCACHE_WB ) +ceph_fscache_unuse_cookie (inode, true);clear_inode (inode);ceph_fscache_unregister_inode_cookie (ci);
@@ -635,6 +637,12 @@ int ceph_fill_file_size(struct inode *inode, int issued, }i_size_write (inode, size); inode->i_blocks =calc_inode_blocks (size); +/* +* If we're truncating up, then we should be able to just update +* the existing cookie. +*/ +if (size > isize) +ceph_fscache_update (inode); ci->i_reported_size = size;if (truncate_seq != ci->i_truncate_seq) { dout("truncate_seq %u -> %u\n",
@@ -667,10 +675,6 @@ int ceph_fill_file_size(struct inode *inode, int issued, truncate_size); ci->i_truncate_size = truncate_size; } - -if (queue_trunc) -ceph_fscache_invalidate (inode); -return queue_trunc; }
@@ -1054,6 +1058,8 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,spin_unlock (&ci->i_ceph_lock); +ceph_fscache_register_inode_cookie (inode); +if (fill_inline)ceph_fill_inline_data (inode, locked_page, iinfo->inline_data, iinfo->inline_len);
@@ -1815,11 +1821,13 @@ bool ceph_inode_set_size(struct inode *inode, loff_t size)spin_lock (&ci->i_ceph_lock);dout ("set_size %p %llu -> %llu\n" , inode,i_size_read (inode), size);i_size_write (inode, size); +ceph_fscache_update (inode); inode->i_blocks =calc_inode_blocks (size); ret =__ceph_should_report_size (ci);spin_unlock (&ci->i_ceph_lock); +return ret; }
@@ -1845,6 +1853,8 @@ static void ceph_do_invalidate_pages(struct inode *inode)u32 orig_gen;int check =0 ; +ceph_fscache_invalidate (inode, false); +mutex_lock (&ci->i_truncate_mutex);if (ceph_inode_is_shutdown (inode)) {
@@ -1869,7 +1879,7 @@ static void ceph_do_invalidate_pages(struct inode *inode) orig_gen = ci->i_rdcache_gen;spin_unlock (&ci->i_ceph_lock); -ceph_fscache_invalidate (inode); +ceph_fscache_invalidate (inode, false);if (invalidate_inode_pages2 (inode->i_mapping) <0 ) {pr_err ("invalidate_inode_pages2 %llx.%llx failed\n" ,ceph_vinop (inode));
@@ -1938,6 +1948,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) ci->i_truncate_pending, to);spin_unlock (&ci->i_ceph_lock); +ceph_fscache_resize (inode, to);truncate_pagecache (inode, to);spin_lock (&ci->i_ceph_lock);
@@ -2185,7 +2196,6 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)if (inode_dirty_flags)__mark_inode_dirty (inode, inode_dirty_flags); -if (mask) { req->r_inode = inode;ihold (inode);
fs/ceph/super.c +1 -9
@@ -908,16 +908,10 @@ static int __init init_caches(void)if (!ceph_wb_pagevec_pool) goto bad_pagevec_pool; - error =ceph_fscache_register (); -if (error) - goto bad_fscache; -return 0 ; -bad_fscache: -kmem_cache_destroy (ceph_mds_request_cachep); bad_pagevec_pool: -mempool_destroy (ceph_wb_pagevec_pool); +kmem_cache_destroy (ceph_mds_request_cachep); bad_mds_req:kmem_cache_destroy (ceph_dir_file_cachep); bad_dir_file:
@@ -949,8 +943,6 @@ static void destroy_caches(void)kmem_cache_destroy (ceph_dir_file_cachep);kmem_cache_destroy (ceph_mds_request_cachep);mempool_destroy (ceph_wb_pagevec_pool); - -ceph_fscache_unregister (); }static void __ceph_umount_begin (struct ceph_fs_client *fsc)
fs/ceph/super.h +1 -1
@@ -139,7 +139,7 @@ struct ceph_fs_client {#endif #ifdef CONFIG_CEPH_FSCACHE -struct fscache_cookie *fscache; +struct fscache_volume *fscache;#endif };
Signature · 1 line
2.33.1
Re: [PATCH 1/2] ceph: conversion to new fscache API
fs/ceph/cache.c line 57
Quoted 7 lines · Jeff Layton
fs/ceph/cache.c +4
-1+void ceph_fscache_unregister_inode_cookie (struct ceph_inode_info * ci) { -return fscache_register_netfs (&ceph_cache_netfs); +struct fscache_cookie * cookie =xchg (&ci->fscache,NULL ); + +fscache_relinquish_cookie (cookie, false); }
xchg() should be excessive there. This is only called from ceph_evict_inode(). Also, if you're going to reset the pointer, it might be worth poisoning it rather than nulling it.
David
Re: [PATCH 1/2] ceph: conversion to new fscache API
Quoted 14 lines · David Howells
fs/ceph/cache.c line 57
Quoted 7 lines · Jeff Layton
fs/ceph/cache.c +4
-1+void ceph_fscache_unregister_inode_cookie (struct ceph_inode_info * ci) { -return fscache_register_netfs (&ceph_cache_netfs); +struct fscache_cookie * cookie =xchg (&ci->fscache,NULL ); + +fscache_relinquish_cookie (cookie, false); }
xchg()should be excessive there. This is only called fromceph_evict_inode(). Also, if you're going to reset the pointer, it might be worth poisoning it rather than nulling it.
Ok, makes sense. I'll make that change soon.
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH 1/2] ceph: conversion to new fscache API
In reply to Jeff LaytonQuoted 1 line · Jeff Layton
if (!(gfp &
__GFP_DIRECT_RECLAIM) || !(gfp &__GFP_FS))
There's a function for the first part of this:
if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS))
fs/ceph/cache.c int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc) · line 144
Quoted 5 lines
fs/ceph/cache.c +1
-0+ fsc->fscache =fscache_acquire_volume (name,NULL ,0 );if (fsc->fscache) { ent->fscache = fsc->fscache;list_add_tail (&ent->list, &ceph_fscache_list);
It shouldn't really be necessary to have ceph_fscache_list since fscache_acquire_volume() will do it's own duplicate check. I wonder if I should make fscache_acquire_volume() return -EEXIST or -EBUSY rather than NULL in such a case and not print an error, but rather leave that to the filesystem to display.
That would allow you to get rid of the ceph_fscache_entry struct also, I think.
Quoted 1 line
+#define
FSCACHE_USE_NEW_IO_API
That doesn't exist anymore.
fs/ceph/inode.c int ceph_fill_file_size(struct inode *inode, int issued, · line 645
Quoted 6 lines
fs/ceph/inode.c +6
-0+/* +* If we're truncating up, then we should be able to just update +* the existing cookie. +*/ +if (size > isize) +ceph_fscache_update (inode);
Might look better to say "expanding" rather than "truncating up".
David
Re: [PATCH 1/2] ceph: conversion to new fscache API
Quoted 20 lines · David Howells
Quoted 1 line · Jeff Layton
if (!(gfp &
__GFP_DIRECT_RECLAIM) || !(gfp &__GFP_FS))There's a function for the first part of this:
if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS))fs/ceph/cache.c int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc) · line 144
Quoted 5 lines
fs/ceph/cache.c +1
-0+ fsc->fscache =fscache_acquire_volume (name,NULL ,0 );if (fsc->fscache) { ent->fscache = fsc->fscache;list_add_tail (&ent->list, &ceph_fscache_list);It shouldn't really be necessary to have
ceph_fscache_listsincefscache_acquire_volume()will do it's own duplicate check. I wonder if I should makefscache_acquire_volume()return-EEXISTor-EBUSYrather than NULL in such a case and not print an error, but rather leave that to the filesystem to display.
Quoted 3 lines
That would allow you to get rid of the
ceph_fscache_entrystruct also, I think.
Returning an error there sounds like a better thing to do.
I'll make the other changes you suggested now. Let me know if you change the fscache_acquire_volume return.
Quoted 15 lines
Quoted 1 line
+#define
FSCACHE_USE_NEW_IO_APIThat doesn't exist anymore.
fs/ceph/inode.c int ceph_fill_file_size(struct inode *inode, int issued, · line 645
Quoted 6 lines
fs/ceph/inode.c +6
-0+/* +* If we're truncating up, then we should be able to just update +* the existing cookie. +*/ +if (size > isize) +ceph_fscache_update (inode);Might look better to say "expanding" rather than "truncating up".
David
Signature · 1 line
Jeff Layton <[email protected]>
PATCH 2/2 ceph: add fscache writeback support
In reply to Jeff LaytonWhen updating the backing store from the pagecache (a'la writepage or writepages), write to the cache first. This allows us to keep caching files even when they are being written, as long as we have appropriate caps.
--- fs/ceph/addr.c | 67 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 8 deletions(-)
fs/ceph/addr.c +59 -8
@@ -5,7 +5,6 @@#include <linux/fs.h> #include <linux/mm.h> #include <linux/pagemap.h> -#include <linux/writeback.h> /* generic_writepages */ #include <linux/slab.h> #include <linux/pagevec.h> #include <linux/task_io_accounting_ops.h>
@@ -384,6 +383,38 @@ static void ceph_readahead(struct readahead_control *ractl)netfs_readahead (ractl, &ceph_netfs_read_ops, (void *)(uintptr_t )got); } +#ifdef CONFIG_CEPH_FSCACHE +static void ceph_set_page_fscache (struct page *page) +{ +set_page_fscache (page); +} + +static void ceph_fscache_write_terminated (void *priv,ssize_t error,bool was_async) +{ +struct inode *inode = priv; + +if (IS_ERR_VALUE (error) && error != -ENOBUFS ) +ceph_fscache_invalidate (inode, false); +} + +static void ceph_fscache_write_to_cache (struct inode *inode,u64 off,u64 len,bool caching) +{ +struct ceph_inode_info *ci =ceph_inode (inode); +struct fscache_cookie *cookie =ceph_fscache_cookie (ci); + +fscache_write_to_cache (cookie, inode->i_mapping, off, len,i_size_read (inode), + ceph_fscache_write_terminated, inode, caching); +} +#else +static inline void ceph_set_page_fscache (struct page *page) +{ +} + +static inline void ceph_fscache_write_to_cache (struct inode *inode,u64 off,u64 len,bool caching) +{ +} +#endif /* CONFIG_CEPH_FSCACHE */ +struct ceph_writeback_ctl {loff_t i_size;
@@ -499,6 +530,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)struct ceph_writeback_ctl ceph_wbc;struct ceph_osd_client *osdc = &fsc->client->osdc;struct ceph_osd_request *req; +bool caching =ceph_is_cache_enabled (inode);dout ("writepage %p idx %lu\n" , page, page->index);
@@ -537,16 +569,17 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) CONGESTION_ON_THRESH(fsc->mount_options->congestion_kb))set_bdi_congested (inode_to_bdi (inode),BLK_RW_ASYNC ); -set_page_writeback (page); req =ceph_osdc_new_request (osdc, &ci->i_layout,ceph_vino (inode), page_off, &len,0 ,1 ,CEPH_OSD_OP_WRITE ,CEPH_OSD_FLAG_WRITE , snapc, ceph_wbc.truncate_seq, ceph_wbc.truncate_size, true); -if (IS_ERR (req)) { -redirty_page_for_writepage (wbc, page); -end_page_writeback (page); +if (IS_ERR (req))return PTR_ERR (req); - } + +set_page_writeback (page); +if (caching) +ceph_set_page_fscache (page); +ceph_fscache_write_to_cache (inode, page_off, len, caching);/* it may be a short write due to an object boundary */ WARN_ON_ONCE (len >thp_size (page));
@@ -605,6 +638,9 @@ static int ceph_writepage(struct page *page, struct writeback_control *wbc)struct inode *inode = page->mapping->host;BUG_ON (!inode);ihold (inode); + +wait_on_page_fscache (page); + err =writepage_nounlock (page, wbc);if (err == -ERESTARTSYS ) {/* direct memory reclaimer was killed by SIGKILL. return 0
@@ -726,6 +762,7 @@ static int ceph_writepages_start(struct address_space *mapping,struct ceph_writeback_ctl ceph_wbc;bool should_loop, range_whole = false;bool done = false; +bool caching =ceph_is_cache_enabled (inode); dout("writepages_start %p (mode=%s)\n", inode, wbc->sync_mode == WB_SYNC_NONE ? "NONE" :
@@ -849,7 +886,7 @@ static int ceph_writepages_start(struct address_space *mapping,unlock_page (page);break ; } -if (PageWriteback (page)) { +if (PageWriteback (page) ||PageFsCache (page)) {if (wbc->sync_mode ==WB_SYNC_NONE ) {dout ("%p under writeback\n" , page);unlock_page (page);
@@ -857,6 +894,7 @@ static int ceph_writepages_start(struct address_space *mapping, }dout ("waiting on writeback %p\n" , page);wait_on_page_writeback (page); +wait_on_page_fscache (page); }if (!clear_page_dirty_for_io (page)) {
@@ -989,9 +1027,19 @@ static int ceph_writepages_start(struct address_space *mapping, op_idx =0 ;for (i =0 ; i < locked_pages; i++) {u64 cur_offset =page_offset (pages[i]); +/* +* Discontinuity in page range? Ceph can handle that by just passing +* multiple extents in the write op. +*/ if (offset + len != cur_offset) { +/* If it's full, stop here */ if (op_idx +1 == req->r_num_ops)break ; + +/* Kick off an fscache write with what we have so far. */ +ceph_fscache_write_to_cache (inode, offset, len, caching); + +/* Start a new extent */ osd_req_op_extent_dup_last (req, op_idx, cur_offset - offset); dout("writepages got pages at %llu~%llu\n",
@@ -1002,14 +1050,17 @@ static int ceph_writepages_start(struct address_space *mapping,osd_req_op_extent_update (req, op_idx, len); len =0 ; - offset = cur_offset; + offset = cur_offset; data_pages = pages + i; op_idx++; }set_page_writeback (pages[i]); +if (caching) +ceph_set_page_fscache (pages[i]); len +=thp_size (page); } +ceph_fscache_write_to_cache (inode, offset, len, caching);if (ceph_wbc.size_stable) { len =min (len, ceph_wbc.i_size - offset);
Signature · 1 line
2.33.1
Re: [PATCH 2/2] ceph: add fscache writeback support
fs/ceph/addr.c static int writepage_nounlock(struct page *page, struct writeback_control *wbc) · line 581
Quoted 2 lines · Jeff Layton
fs/ceph/addr.c +2
-0+if (caching) +ceph_set_page_fscache (page);
I suggest moving this test into ceph_set_page_fscache().
David