Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c515dd4 authored by David Howells's avatar David Howells
Browse files

FS-Cache: Add cache management



Implement the entry points by which a cache backend may initialise, add,
declare an error upon and withdraw a cache.

Further, an object is created in sysfs under which each cache added will get
an object created:

	/sys/fs/fscache/<cachetag>/

All of this is described in Documentation/filesystems/caching/backend-api.txt
added by a previous patch.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Acked-by: default avatarSteve Dickson <steved@redhat.com>
Acked-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Acked-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Tested-by: default avatarDaire Byrne <Daire.Byrne@framestore.com>
parent 0e04d4ce
Loading
Loading
Loading
Loading
+249 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

LIST_HEAD(fscache_cache_list);
DECLARE_RWSEM(fscache_addremove_sem);
DECLARE_WAIT_QUEUE_HEAD(fscache_cache_cleared_wq);
EXPORT_SYMBOL(fscache_cache_cleared_wq);

static LIST_HEAD(fscache_cache_tag_list);

@@ -164,3 +166,250 @@ struct fscache_cache *fscache_select_cache_for_object(
	_leave(" = %p [first]", cache);
	return cache;
}

/**
 * fscache_init_cache - Initialise a cache record
 * @cache: The cache record to be initialised
 * @ops: The cache operations to be installed in that record
 * @idfmt: Format string to define identifier
 * @...: sprintf-style arguments
 *
 * Initialise a record of a cache and fill in the name.
 *
 * See Documentation/filesystems/caching/backend-api.txt for a complete
 * description.
 */
void fscache_init_cache(struct fscache_cache *cache,
			const struct fscache_cache_ops *ops,
			const char *idfmt,
			...)
{
	va_list va;

	memset(cache, 0, sizeof(*cache));

	cache->ops = ops;

	va_start(va, idfmt);
	vsnprintf(cache->identifier, sizeof(cache->identifier), idfmt, va);
	va_end(va);

	INIT_WORK(&cache->op_gc, NULL);
	INIT_LIST_HEAD(&cache->link);
	INIT_LIST_HEAD(&cache->object_list);
	INIT_LIST_HEAD(&cache->op_gc_list);
	spin_lock_init(&cache->object_list_lock);
	spin_lock_init(&cache->op_gc_list_lock);
}
EXPORT_SYMBOL(fscache_init_cache);

/**
 * fscache_add_cache - Declare a cache as being open for business
 * @cache: The record describing the cache
 * @ifsdef: The record of the cache object describing the top-level index
 * @tagname: The tag describing this cache
 *
 * Add a cache to the system, making it available for netfs's to use.
 *
 * See Documentation/filesystems/caching/backend-api.txt for a complete
 * description.
 */
int fscache_add_cache(struct fscache_cache *cache,
		      struct fscache_object *ifsdef,
		      const char *tagname)
{
	struct fscache_cache_tag *tag;

	BUG_ON(!cache->ops);
	BUG_ON(!ifsdef);

	cache->flags = 0;
	ifsdef->event_mask = ULONG_MAX & ~(1 << FSCACHE_OBJECT_EV_CLEARED);
	ifsdef->state = FSCACHE_OBJECT_ACTIVE;

	if (!tagname)
		tagname = cache->identifier;

	BUG_ON(!tagname[0]);

	_enter("{%s.%s},,%s", cache->ops->name, cache->identifier, tagname);

	/* we use the cache tag to uniquely identify caches */
	tag = __fscache_lookup_cache_tag(tagname);
	if (IS_ERR(tag))
		goto nomem;

	if (test_and_set_bit(FSCACHE_TAG_RESERVED, &tag->flags))
		goto tag_in_use;

	cache->kobj = kobject_create_and_add(tagname, fscache_root);
	if (!cache->kobj)
		goto error;

	ifsdef->cookie = &fscache_fsdef_index;
	ifsdef->cache = cache;
	cache->fsdef = ifsdef;

	down_write(&fscache_addremove_sem);

	tag->cache = cache;
	cache->tag = tag;

	/* add the cache to the list */
	list_add(&cache->link, &fscache_cache_list);

	/* add the cache's netfs definition index object to the cache's
	 * list */
	spin_lock(&cache->object_list_lock);
	list_add_tail(&ifsdef->cache_link, &cache->object_list);
	spin_unlock(&cache->object_list_lock);

	/* add the cache's netfs definition index object to the top level index
	 * cookie as a known backing object */
	spin_lock(&fscache_fsdef_index.lock);

	hlist_add_head(&ifsdef->cookie_link,
		       &fscache_fsdef_index.backing_objects);

	atomic_inc(&fscache_fsdef_index.usage);

	/* done */
	spin_unlock(&fscache_fsdef_index.lock);
	up_write(&fscache_addremove_sem);

	printk(KERN_NOTICE "FS-Cache: Cache \"%s\" added (type %s)\n",
	       cache->tag->name, cache->ops->name);
	kobject_uevent(cache->kobj, KOBJ_ADD);

	_leave(" = 0 [%s]", cache->identifier);
	return 0;

tag_in_use:
	printk(KERN_ERR "FS-Cache: Cache tag '%s' already in use\n", tagname);
	__fscache_release_cache_tag(tag);
	_leave(" = -EXIST");
	return -EEXIST;

error:
	__fscache_release_cache_tag(tag);
	_leave(" = -EINVAL");
	return -EINVAL;

nomem:
	_leave(" = -ENOMEM");
	return -ENOMEM;
}
EXPORT_SYMBOL(fscache_add_cache);

/**
 * fscache_io_error - Note a cache I/O error
 * @cache: The record describing the cache
 *
 * Note that an I/O error occurred in a cache and that it should no longer be
 * used for anything.  This also reports the error into the kernel log.
 *
 * See Documentation/filesystems/caching/backend-api.txt for a complete
 * description.
 */
void fscache_io_error(struct fscache_cache *cache)
{
	set_bit(FSCACHE_IOERROR, &cache->flags);

	printk(KERN_ERR "FS-Cache: Cache %s stopped due to I/O error\n",
	       cache->ops->name);
}
EXPORT_SYMBOL(fscache_io_error);

/*
 * request withdrawal of all the objects in a cache
 * - all the objects being withdrawn are moved onto the supplied list
 */
static void fscache_withdraw_all_objects(struct fscache_cache *cache,
					 struct list_head *dying_objects)
{
	struct fscache_object *object;

	spin_lock(&cache->object_list_lock);

	while (!list_empty(&cache->object_list)) {
		object = list_entry(cache->object_list.next,
				    struct fscache_object, cache_link);
		list_move_tail(&object->cache_link, dying_objects);

		_debug("withdraw %p", object->cookie);

		spin_lock(&object->lock);
		spin_unlock(&cache->object_list_lock);
		fscache_raise_event(object, FSCACHE_OBJECT_EV_WITHDRAW);
		spin_unlock(&object->lock);

		cond_resched();
		spin_lock(&cache->object_list_lock);
	}

	spin_unlock(&cache->object_list_lock);
}

/**
 * fscache_withdraw_cache - Withdraw a cache from the active service
 * @cache: The record describing the cache
 *
 * Withdraw a cache from service, unbinding all its cache objects from the
 * netfs cookies they're currently representing.
 *
 * See Documentation/filesystems/caching/backend-api.txt for a complete
 * description.
 */
void fscache_withdraw_cache(struct fscache_cache *cache)
{
	LIST_HEAD(dying_objects);

	_enter("");

	printk(KERN_NOTICE "FS-Cache: Withdrawing cache \"%s\"\n",
	       cache->tag->name);

	/* make the cache unavailable for cookie acquisition */
	if (test_and_set_bit(FSCACHE_CACHE_WITHDRAWN, &cache->flags))
		BUG();

	down_write(&fscache_addremove_sem);
	list_del_init(&cache->link);
	cache->tag->cache = NULL;
	up_write(&fscache_addremove_sem);

	/* make sure all pages pinned by operations on behalf of the netfs are
	 * written to disk */
	cache->ops->sync_cache(cache);

	/* dissociate all the netfs pages backed by this cache from the block
	 * mappings in the cache */
	cache->ops->dissociate_pages(cache);

	/* we now have to destroy all the active objects pertaining to this
	 * cache - which we do by passing them off to thread pool to be
	 * disposed of */
	_debug("destroy");

	fscache_withdraw_all_objects(cache, &dying_objects);

	/* wait for all extant objects to finish their outstanding operations
	 * and go away */
	_debug("wait for finish");
	wait_event(fscache_cache_cleared_wq,
		   atomic_read(&cache->object_count) == 0);
	_debug("wait for clearance");
	wait_event(fscache_cache_cleared_wq,
		   list_empty(&cache->object_list));
	_debug("cleared");
	ASSERT(list_empty(&dying_objects));

	kobject_put(cache->kobj);

	clear_bit(FSCACHE_TAG_RESERVED, &cache->tag->flags);
	fscache_release_cache_tag(cache->tag);
	cache->tag = NULL;

	_leave("");
}
EXPORT_SYMBOL(fscache_withdraw_cache);
+7 −0
Original line number Diff line number Diff line
@@ -56,9 +56,15 @@ static int __init fscache_init(void)
	if (ret < 0)
		goto error_proc;

	fscache_root = kobject_create_and_add("fscache", kernel_kobj);
	if (!fscache_root)
		goto error_kobj;

	printk(KERN_NOTICE "FS-Cache: Loaded\n");
	return 0;

error_kobj:
	fscache_proc_cleanup();
error_proc:
	slow_work_unregister_user();
error_slow_work:
@@ -74,6 +80,7 @@ static void __exit fscache_exit(void)
{
	_enter("");

	kobject_put(fscache_root);
	fscache_proc_cleanup();
	slow_work_unregister_user();
	printk(KERN_NOTICE "FS-Cache: Unloaded\n");