Commit 400e1286 authored by Jeff Layton's avatar Jeff Layton Committed by David Howells
Browse files

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.

Signed-off-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20211129162907.149445-2-jlayton@kernel.org/ # v1
Link: https://lore.kernel.org/r/20211207134451.66296-2-jlayton@kernel.org/ # v2
Link: https://lore.kernel.org/r/163906984277.143852.14697110691303589000.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967188351.1823006.5065634844099079351.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021581427.640689.14128682147127509264.stgit@warthog.procyon.org.uk/ # v4
parent 16f2f4e6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+20 −14
Original line number Diff line number Diff line
@@ -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,9 +151,7 @@ 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);

@@ -164,17 +160,27 @@ static void ceph_invalidatepage(struct page *page, unsigned int offset,
		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))
		if (!gfpflags_allow_blocking(gfp) || !(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)
+59 −159
Original line number Diff line number Diff line
@@ -12,199 +12,99 @@
#include "super.h"
#include "cache.h"

struct fscache_netfs ceph_cache_netfs = {
	.name		= "ceph",
	.version	= 0,
};

static DEFINE_MUTEX(ceph_fscache_lock);
static LIST_HEAD(ceph_fscache_list);

struct ceph_fscache_entry {
	struct list_head list;
	struct fscache_cookie *fscache;
	size_t uniq_len;
	/* The following members must be last */
	struct ceph_fsid fsid;
	char uniquifier[];
};

static const struct fscache_cookie_def ceph_fscache_fsid_object_def = {
	.name		= "CEPH.fsid",
	.type		= FSCACHE_COOKIE_TYPE_INDEX,
};

int __init ceph_fscache_register(void)
void ceph_fscache_register_inode_cookie(struct inode *inode)
{
	return fscache_register_netfs(&ceph_cache_netfs);
}
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);

void ceph_fscache_unregister(void)
{
	fscache_unregister_netfs(&ceph_cache_netfs);
}
	/* No caching for filesystem? */
	if (!fsc->fscache)
		return;

int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc)
{
	const struct ceph_fsid *fsid = &fsc->client->fsid;
	const char *fscache_uniq = fsc->mount_options->fscache_uniq;
	size_t uniq_len = fscache_uniq ? strlen(fscache_uniq) : 0;
	struct ceph_fscache_entry *ent;
	int err = 0;
	/* Regular files only */
	if (!S_ISREG(inode->i_mode))
		return;

	mutex_lock(&ceph_fscache_lock);
	list_for_each_entry(ent, &ceph_fscache_list, list) {
		if (memcmp(&ent->fsid, fsid, sizeof(*fsid)))
			continue;
		if (ent->uniq_len != uniq_len)
			continue;
		if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len))
			continue;

		errorfc(fc, "fscache cookie already registered for fsid %pU, use fsc=<uniquifier> option",
		       fsid);
		err = -EBUSY;
		goto out_unlock;
	}
	/* Only new inodes! */
	if (!(inode->i_state & I_NEW))
		return;

	ent = kzalloc(sizeof(*ent) + uniq_len, GFP_KERNEL);
	if (!ent) {
		err = -ENOMEM;
		goto out_unlock;
	}
	WARN_ON_ONCE(ci->fscache);

	memcpy(&ent->fsid, fsid, sizeof(*fsid));
	if (uniq_len > 0) {
		memcpy(&ent->uniquifier, fscache_uniq, uniq_len);
		ent->uniq_len = uniq_len;
	ci->fscache = fscache_acquire_cookie(fsc->fscache, 0,
					     &ci->i_vino, sizeof(ci->i_vino),
					     &ci->i_version, sizeof(ci->i_version),
					     i_size_read(inode));
}

	fsc->fscache = fscache_acquire_cookie(ceph_cache_netfs.primary_index,
					      &ceph_fscache_fsid_object_def,
					      &ent->fsid, sizeof(ent->fsid) + uniq_len,
					      NULL, 0,
					      fsc, 0, true);
void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci)
{
	struct fscache_cookie *cookie = ci->fscache;

	if (fsc->fscache) {
		ent->fscache = fsc->fscache;
		list_add_tail(&ent->list, &ceph_fscache_list);
	} else {
		kfree(ent);
		errorfc(fc, "unable to register fscache cookie for fsid %pU",
		       fsid);
		/* all other fs ignore this error */
	}
out_unlock:
	mutex_unlock(&ceph_fscache_lock);
	return err;
	fscache_relinquish_cookie(cookie, false);
}

static enum fscache_checkaux ceph_fscache_inode_check_aux(
	void *cookie_netfs_data, const void *data, uint16_t dlen,
	loff_t object_size)
void ceph_fscache_use_cookie(struct inode *inode, bool will_modify)
{
	struct ceph_inode_info* ci = cookie_netfs_data;
	struct inode* inode = &ci->vfs_inode;

	if (dlen != sizeof(ci->i_version) ||
	    i_size_read(inode) != object_size)
		return FSCACHE_CHECKAUX_OBSOLETE;

	if (*(u64 *)data != ci->i_version)
		return FSCACHE_CHECKAUX_OBSOLETE;
	struct ceph_inode_info *ci = ceph_inode(inode);

	dout("ceph inode 0x%p cached okay\n", ci);
	return FSCACHE_CHECKAUX_OKAY;
	fscache_use_cookie(ci->fscache, will_modify);
}

static const struct fscache_cookie_def ceph_fscache_inode_object_def = {
	.name		= "CEPH.inode",
	.type		= FSCACHE_COOKIE_TYPE_DATAFILE,
	.check_aux	= ceph_fscache_inode_check_aux,
};

void ceph_fscache_register_inode_cookie(struct inode *inode)
void ceph_fscache_unuse_cookie(struct inode *inode, bool update)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);

	/* No caching for filesystem */
	if (!fsc->fscache)
		return;

	/* Only cache for regular files that are read only */
	if (!S_ISREG(inode->i_mode))
		return;
	if (update) {
		loff_t i_size = i_size_read(inode);

	inode_lock_nested(inode, I_MUTEX_CHILD);
	if (!ci->fscache) {
		ci->fscache = fscache_acquire_cookie(fsc->fscache,
						     &ceph_fscache_inode_object_def,
						     &ci->i_vino, sizeof(ci->i_vino),
						     &ci->i_version, sizeof(ci->i_version),
						     ci, i_size_read(inode), false);
		fscache_unuse_cookie(ci->fscache, &ci->i_version, &i_size);
	} else {
		fscache_unuse_cookie(ci->fscache, NULL, NULL);
	}
	inode_unlock(inode);
}

void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci)
void ceph_fscache_update(struct inode *inode)
{
	struct fscache_cookie* cookie;

	if ((cookie = ci->fscache) == NULL)
		return;

	ci->fscache = NULL;
	struct ceph_inode_info *ci = ceph_inode(inode);
	loff_t i_size = i_size_read(inode);

	fscache_relinquish_cookie(cookie, &ci->i_vino, false);
	fscache_update_cookie(ci->fscache, &ci->i_version, &i_size);
}

static bool ceph_fscache_can_enable(void *data)
void ceph_fscache_invalidate(struct inode *inode, bool dio_write)
{
	struct inode *inode = data;
	return !inode_is_open_for_write(inode);
	struct ceph_inode_info *ci = ceph_inode(inode);

	fscache_invalidate(ceph_inode(inode)->fscache,
			   &ci->i_version, i_size_read(inode),
			   dio_write ? FSCACHE_INVAL_DIO_WRITE : 0);
}

void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp)
int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	const struct ceph_fsid *fsid = &fsc->client->fsid;
	const char *fscache_uniq = fsc->mount_options->fscache_uniq;
	size_t uniq_len = fscache_uniq ? strlen(fscache_uniq) : 0;
	char *name;
	int err = 0;

	if (!fscache_cookie_valid(ci->fscache))
		return;
	name = kasprintf(GFP_KERNEL, "ceph,%pU%s%s", fsid, uniq_len ? "," : "",
			 uniq_len ? fscache_uniq : "");
	if (!name)
		return -ENOMEM;

	if (inode_is_open_for_write(inode)) {
		dout("fscache_file_set_cookie %p %p disabling cache\n",
		     inode, filp);
		fscache_disable_cookie(ci->fscache, &ci->i_vino, false);
	} else {
		fscache_enable_cookie(ci->fscache, &ci->i_vino, i_size_read(inode),
				      ceph_fscache_can_enable, inode);
		if (fscache_cookie_enabled(ci->fscache)) {
			dout("fscache_file_set_cookie %p %p enabling cache\n",
			     inode, filp);
		}
	fsc->fscache = fscache_acquire_volume(name, NULL, NULL, 0);
	if (IS_ERR_OR_NULL(fsc->fscache)) {
		errorfc(fc, "Unable to register fscache cookie for %s", name);
		err = fsc->fscache ? PTR_ERR(fsc->fscache) : -EOPNOTSUPP;
		fsc->fscache = NULL;
	}
	kfree(name);
	return err;
}

void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc)
{
	if (fscache_cookie_valid(fsc->fscache)) {
		struct ceph_fscache_entry *ent;
		bool found = false;

		mutex_lock(&ceph_fscache_lock);
		list_for_each_entry(ent, &ceph_fscache_list, list) {
			if (ent->fscache == fsc->fscache) {
				list_del(&ent->list);
				kfree(ent);
				found = true;
				break;
			}
		}
		WARN_ON_ONCE(!found);
		mutex_unlock(&ceph_fscache_lock);

		__fscache_relinquish_cookie(fsc->fscache, NULL, false);
	}
	fsc->fscache = NULL;
	fscache_relinquish_volume(fsc->fscache, NULL, false);
}
+68 −29
Original line number Diff line number Diff line
@@ -12,19 +12,19 @@
#include <linux/netfs.h>

#ifdef CONFIG_CEPH_FSCACHE

extern struct fscache_netfs ceph_cache_netfs;

int ceph_fscache_register(void);
void ceph_fscache_unregister(void);
#include <linux/fscache.h>

int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc);
void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc);

void ceph_fscache_register_inode_cookie(struct inode *inode);
void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci);
void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp);
void ceph_fscache_revalidate_cookie(struct ceph_inode_info *ci);

void ceph_fscache_use_cookie(struct inode *inode, bool will_modify);
void ceph_fscache_unuse_cookie(struct inode *inode, bool update);

void ceph_fscache_update(struct inode *inode);
void ceph_fscache_invalidate(struct inode *inode, bool dio_write);

static inline void ceph_fscache_inode_init(struct ceph_inode_info *ci)
{
@@ -36,37 +36,51 @@ static inline struct fscache_cookie *ceph_fscache_cookie(struct ceph_inode_info
	return ci->fscache;
}

static inline void ceph_fscache_invalidate(struct inode *inode)
static inline void ceph_fscache_resize(struct inode *inode, loff_t to)
{
	fscache_invalidate(ceph_inode(inode)->fscache);
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct fscache_cookie *cookie = ceph_fscache_cookie(ci);

	if (cookie) {
		ceph_fscache_use_cookie(inode, true);
		fscache_resize_cookie(cookie, to);
		ceph_fscache_unuse_cookie(inode, true);
	}
}

static inline bool ceph_is_cache_enabled(struct inode *inode)
static inline void ceph_fscache_unpin_writeback(struct inode *inode,
						struct writeback_control *wbc)
{
	struct fscache_cookie *cookie = ceph_fscache_cookie(ceph_inode(inode));
	fscache_unpin_writeback(wbc, ceph_fscache_cookie(ceph_inode(inode)));
}

	if (!cookie)
		return false;
	return fscache_cookie_enabled(cookie);
static inline int ceph_fscache_set_page_dirty(struct page *page)
{
	struct inode *inode = page->mapping->host;
	struct ceph_inode_info *ci = ceph_inode(inode);

	return fscache_set_page_dirty(page, ceph_fscache_cookie(ci));
}

static inline int ceph_begin_cache_operation(struct netfs_read_request *rreq)
{
	struct fscache_cookie *cookie = ceph_fscache_cookie(ceph_inode(rreq->inode));

	return fscache_begin_read_operation(rreq, cookie);
	return fscache_begin_read_operation(&rreq->cache_resources, cookie);
}
#else

static inline int ceph_fscache_register(void)
static inline bool ceph_is_cache_enabled(struct inode *inode)
{
	return 0;
	return fscache_cookie_enabled(ceph_fscache_cookie(ceph_inode(inode)));
}

static inline void ceph_fscache_unregister(void)
static inline void ceph_fscache_note_page_release(struct inode *inode)
{
}
	struct ceph_inode_info *ci = ceph_inode(inode);

	fscache_note_page_release(ceph_fscache_cookie(ci));
}
#else /* CONFIG_CEPH_FSCACHE */
static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc,
					   struct fs_context *fc)
{
@@ -81,28 +95,49 @@ static inline void ceph_fscache_inode_init(struct ceph_inode_info *ci)
{
}

static inline struct fscache_cookie *ceph_fscache_cookie(struct ceph_inode_info *ci)
static inline void ceph_fscache_register_inode_cookie(struct inode *inode)
{
	return NULL;
}

static inline void ceph_fscache_register_inode_cookie(struct inode *inode)
static inline void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci)
{
}

static inline void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci)
static inline void ceph_fscache_use_cookie(struct inode *inode, bool will_modify)
{
}

static inline void ceph_fscache_file_set_cookie(struct inode *inode,
						struct file *filp)
static inline void ceph_fscache_unuse_cookie(struct inode *inode, bool update)
{
}

static inline void ceph_fscache_invalidate(struct inode *inode)
static inline void ceph_fscache_update(struct inode *inode)
{
}

static inline void ceph_fscache_invalidate(struct inode *inode, bool dio_write)
{
}

static inline struct fscache_cookie *ceph_fscache_cookie(struct ceph_inode_info *ci)
{
	return NULL;
}

static inline void ceph_fscache_resize(struct inode *inode, loff_t to)
{
}

static inline void ceph_fscache_unpin_writeback(struct inode *inode,
						struct writeback_control *wbc)
{
}

static inline int ceph_fscache_set_page_dirty(struct page *page)
{
	return __set_page_dirty_nobuffers(page);
}

static inline bool ceph_is_cache_enabled(struct inode *inode)
{
	return false;
@@ -112,6 +147,10 @@ static inline int ceph_begin_cache_operation(struct netfs_read_request *rreq)
{
	return -ENOBUFS;
}
#endif

#endif /* _CEPH_CACHE_H */
static inline void ceph_fscache_note_page_release(struct inode *inode)
{
}
#endif /* CONFIG_CEPH_FSCACHE */

#endif
+2 −1
Original line number Diff line number Diff line
@@ -1856,7 +1856,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);

@@ -2388,6 +2388,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)
Loading