Commit 1702e797 authored by Jeff Layton's avatar Jeff Layton Committed by David Howells
Browse files

ceph: add fscache writeback support

parent 400e1286
Loading
Loading
Loading
Loading
+59 −8
Original line number Diff line number Diff line
@@ -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",
@@ -1008,8 +1056,11 @@ static int ceph_writepages_start(struct address_space *mapping,
			}

			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);