]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ceph: add fscache writeback support
authorJeff Layton <jlayton@kernel.org>
Tue, 7 Dec 2021 13:44:51 +0000 (08:44 -0500)
committerDavid Howells <dhowells@redhat.com>
Tue, 11 Jan 2022 22:13:01 +0000 (22:13 +0000)
When 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.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20211129162907.149445-3-jlayton@kernel.org/
Link: https://lore.kernel.org/r/20211207134451.66296-3-jlayton@kernel.org/
Link: https://lore.kernel.org/r/163906985808.143852.1383891557313186623.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/163967190257.1823006.16713609520911954804.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/164021585020.640689.6765214932458435472.stgit@warthog.procyon.org.uk/
fs/ceph/addr.c

index 0ffc4c8d7c10a2a304d76ffaaf5ca5d26698dda4..e836f8f1d4f846545620c88f6fec190d10f05d78 100644 (file)
@@ -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 @@ get_more_pages:
                                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 @@ get_more_pages:
                                }
                                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 @@ new_request:
                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 @@ new_request:
                                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);