diff options
Diffstat (limited to 'fs/fscache')
| -rw-r--r-- | fs/fscache/cache.c | 51 | ||||
| -rw-r--r-- | fs/fscache/cookie.c | 369 | ||||
| -rw-r--r-- | fs/fscache/fsdef.c | 2 | ||||
| -rw-r--r-- | fs/fscache/histogram.c | 6 | ||||
| -rw-r--r-- | fs/fscache/internal.h | 58 | ||||
| -rw-r--r-- | fs/fscache/main.c | 22 | ||||
| -rw-r--r-- | fs/fscache/netfs.c | 9 | ||||
| -rw-r--r-- | fs/fscache/object-list.c | 116 | ||||
| -rw-r--r-- | fs/fscache/object.c | 1113 | ||||
| -rw-r--r-- | fs/fscache/operation.c | 166 | ||||
| -rw-r--r-- | fs/fscache/page.c | 381 | ||||
| -rw-r--r-- | fs/fscache/stats.c | 19 | 
12 files changed, 1476 insertions, 836 deletions
diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index 6a3c48abd67..56cce7fdd39 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c @@ -115,7 +115,7 @@ struct fscache_cache *fscache_select_cache_for_object(  				     struct fscache_object, cookie_link);  		cache = object->cache; -		if (object->state >= FSCACHE_OBJECT_DYING || +		if (fscache_object_is_dying(object) ||  		    test_bit(FSCACHE_IOERROR, &cache->flags))  			cache = NULL; @@ -224,8 +224,10 @@ int fscache_add_cache(struct fscache_cache *cache,  	BUG_ON(!ifsdef);  	cache->flags = 0; -	ifsdef->event_mask = ULONG_MAX & ~(1 << FSCACHE_OBJECT_EV_CLEARED); -	ifsdef->state = FSCACHE_OBJECT_ACTIVE; +	ifsdef->event_mask = +		((1 << NR_FSCACHE_OBJECT_EVENTS) - 1) & +		~(1 << FSCACHE_OBJECT_EV_CLEARED); +	__set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &ifsdef->flags);  	if (!tagname)  		tagname = cache->identifier; @@ -278,15 +280,15 @@ int fscache_add_cache(struct fscache_cache *cache,  	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); +	pr_notice("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); +	pr_err("Cache tag '%s' already in use\n", tagname);  	__fscache_release_cache_tag(tag);  	_leave(" = -EXIST");  	return -EEXIST; @@ -314,10 +316,9 @@ EXPORT_SYMBOL(fscache_add_cache);   */  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); +	if (!test_and_set_bit(FSCACHE_IOERROR, &cache->flags)) +		pr_err("Cache '%s' stopped due to I/O error\n", +		       cache->ops->name);  }  EXPORT_SYMBOL(fscache_io_error); @@ -330,25 +331,25 @@ static void fscache_withdraw_all_objects(struct fscache_cache *cache,  {  	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); +		spin_lock(&cache->object_list_lock); -		_debug("withdraw %p", object->cookie); +		if (!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); -		spin_lock(&object->lock); -		spin_unlock(&cache->object_list_lock); -		fscache_raise_event(object, FSCACHE_OBJECT_EV_WITHDRAW); -		spin_unlock(&object->lock); +			_debug("withdraw %p", object->cookie); +			/* This must be done under object_list_lock to prevent +			 * a race with fscache_drop_object(). +			 */ +			fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); +		} + +		spin_unlock(&cache->object_list_lock);  		cond_resched(); -		spin_lock(&cache->object_list_lock);  	} - -	spin_unlock(&cache->object_list_lock);  }  /** @@ -367,8 +368,8 @@ void fscache_withdraw_cache(struct fscache_cache *cache)  	_enter(""); -	printk(KERN_NOTICE "FS-Cache: Withdrawing cache \"%s\"\n", -	       cache->tag->name); +	pr_notice("Withdrawing cache \"%s\"\n", +		  cache->tag->name);  	/* make the cache unavailable for cookie acquisition */  	if (test_and_set_bit(FSCACHE_CACHE_WITHDRAWN, &cache->flags)) diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 990535071a8..aec01be91b0 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -58,15 +58,16 @@ void fscache_cookie_init_once(void *_cookie)  struct fscache_cookie *__fscache_acquire_cookie(  	struct fscache_cookie *parent,  	const struct fscache_cookie_def *def, -	void *netfs_data) +	void *netfs_data, +	bool enable)  {  	struct fscache_cookie *cookie;  	BUG_ON(!def); -	_enter("{%s},{%s},%p", +	_enter("{%s},{%s},%p,%u",  	       parent ? (char *) parent->def->name : "<no-parent>", -	       def->name, netfs_data); +	       def->name, netfs_data, enable);  	fscache_stat(&fscache_n_acquires); @@ -95,13 +96,18 @@ struct fscache_cookie *__fscache_acquire_cookie(  	atomic_set(&cookie->usage, 1);  	atomic_set(&cookie->n_children, 0); +	/* We keep the active count elevated until relinquishment to prevent an +	 * attempt to wake up every time the object operations queue quiesces. +	 */ +	atomic_set(&cookie->n_active, 1); +  	atomic_inc(&parent->usage);  	atomic_inc(&parent->n_children);  	cookie->def		= def;  	cookie->parent		= parent;  	cookie->netfs_data	= netfs_data; -	cookie->flags		= 0; +	cookie->flags		= (1 << FSCACHE_COOKIE_NO_DATA_YET);  	/* radix tree insertion won't use the preallocation pool unless it's  	 * told it may not wait */ @@ -119,16 +125,22 @@ struct fscache_cookie *__fscache_acquire_cookie(  		break;  	} -	/* if the object is an index then we need do nothing more here - we -	 * create indices on disk when we need them as an index may exist in -	 * multiple caches */ -	if (cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) { -		if (fscache_acquire_non_index_cookie(cookie) < 0) { -			atomic_dec(&parent->n_children); -			__fscache_cookie_put(cookie); -			fscache_stat(&fscache_n_acquires_nobufs); -			_leave(" = NULL"); -			return NULL; +	if (enable) { +		/* if the object is an index then we need do nothing more here +		 * - we create indices on disk when we need them as an index +		 * may exist in multiple caches */ +		if (cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) { +			if (fscache_acquire_non_index_cookie(cookie) == 0) { +				set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); +			} else { +				atomic_dec(&parent->n_children); +				__fscache_cookie_put(cookie); +				fscache_stat(&fscache_n_acquires_nobufs); +				_leave(" = NULL"); +				return NULL; +			} +		} else { +			set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags);  		}  	} @@ -139,6 +151,39 @@ struct fscache_cookie *__fscache_acquire_cookie(  EXPORT_SYMBOL(__fscache_acquire_cookie);  /* + * Enable a cookie to permit it to accept new operations. + */ +void __fscache_enable_cookie(struct fscache_cookie *cookie, +			     bool (*can_enable)(void *data), +			     void *data) +{ +	_enter("%p", cookie); + +	wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK, +			 fscache_wait_bit, TASK_UNINTERRUPTIBLE); + +	if (test_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags)) +		goto out_unlock; + +	if (can_enable && !can_enable(data)) { +		/* The netfs decided it didn't want to enable after all */ +	} else if (cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) { +		/* Wait for outstanding disablement to complete */ +		__fscache_wait_on_invalidate(cookie); + +		if (fscache_acquire_non_index_cookie(cookie) == 0) +			set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); +	} else { +		set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); +	} + +out_unlock: +	clear_bit_unlock(FSCACHE_COOKIE_ENABLEMENT_LOCK, &cookie->flags); +	wake_up_bit(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK); +} +EXPORT_SYMBOL(__fscache_enable_cookie); + +/*   * acquire a non-index cookie   * - this must make sure the index chain is instantiated and instantiate the   *   object representation too @@ -152,7 +197,7 @@ static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie)  	_enter(""); -	cookie->flags = 1 << FSCACHE_COOKIE_UNAVAILABLE; +	set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags);  	/* now we need to see whether the backing objects for this cookie yet  	 * exist, if not there'll be nothing to search */ @@ -175,10 +220,7 @@ static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie)  	_debug("cache %s", cache->tag->name); -	cookie->flags = -		(1 << FSCACHE_COOKIE_LOOKING_UP) | -		(1 << FSCACHE_COOKIE_CREATING) | -		(1 << FSCACHE_COOKIE_NO_DATA_YET); +	set_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags);  	/* ask the cache to allocate objects for this cookie and its parent  	 * chain */ @@ -205,7 +247,7 @@ static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie)  	/* initiate the process of looking up all the objects in the chain  	 * (done by fscache_initialise_object()) */ -	fscache_enqueue_object(object); +	fscache_raise_event(object, FSCACHE_OBJECT_EV_NEW_CHILD);  	spin_unlock(&cookie->lock); @@ -237,13 +279,12 @@ static int fscache_alloc_object(struct fscache_cache *cache,  				struct fscache_cookie *cookie)  {  	struct fscache_object *object; -	struct hlist_node *_n;  	int ret;  	_enter("%p,%p{%s}", cache, cookie, cookie->def->name);  	spin_lock(&cookie->lock); -	hlist_for_each_entry(object, _n, &cookie->backing_objects, +	hlist_for_each_entry(object, &cookie->backing_objects,  			     cookie_link) {  		if (object->cache == cache)  			goto object_already_extant; @@ -286,7 +327,7 @@ static int fscache_alloc_object(struct fscache_cache *cache,  object_already_extant:  	ret = -ENOBUFS; -	if (object->state >= FSCACHE_OBJECT_DYING) { +	if (fscache_object_is_dead(object)) {  		spin_unlock(&cookie->lock);  		goto error;  	} @@ -311,7 +352,6 @@ static int fscache_attach_object(struct fscache_cookie *cookie,  {  	struct fscache_object *p;  	struct fscache_cache *cache = object->cache; -	struct hlist_node *_n;  	int ret;  	_enter("{%s},{OBJ%x}", cookie->def->name, object->debug_id); @@ -321,9 +361,9 @@ static int fscache_attach_object(struct fscache_cookie *cookie,  	/* there may be multiple initial creations of this object, but we only  	 * want one */  	ret = -EEXIST; -	hlist_for_each_entry(p, _n, &cookie->backing_objects, cookie_link) { +	hlist_for_each_entry(p, &cookie->backing_objects, cookie_link) {  		if (p->cache == object->cache) { -			if (p->state >= FSCACHE_OBJECT_DYING) +			if (fscache_object_is_dying(p))  				ret = -ENOBUFS;  			goto cant_attach_object;  		} @@ -331,10 +371,10 @@ static int fscache_attach_object(struct fscache_cookie *cookie,  	/* pin the parent object */  	spin_lock_nested(&cookie->parent->lock, 1); -	hlist_for_each_entry(p, _n, &cookie->parent->backing_objects, +	hlist_for_each_entry(p, &cookie->parent->backing_objects,  			     cookie_link) {  		if (p->cache == object->cache) { -			if (p->state >= FSCACHE_OBJECT_DYING) { +			if (fscache_object_is_dying(p)) {  				ret = -ENOBUFS;  				spin_unlock(&cookie->parent->lock);  				goto cant_attach_object; @@ -370,12 +410,72 @@ cant_attach_object:  }  /* + * Invalidate an object.  Callable with spinlocks held. + */ +void __fscache_invalidate(struct fscache_cookie *cookie) +{ +	struct fscache_object *object; + +	_enter("{%s}", cookie->def->name); + +	fscache_stat(&fscache_n_invalidates); + +	/* Only permit invalidation of data files.  Invalidating an index will +	 * require the caller to release all its attachments to the tree rooted +	 * there, and if it's doing that, it may as well just retire the +	 * cookie. +	 */ +	ASSERTCMP(cookie->def->type, ==, FSCACHE_COOKIE_TYPE_DATAFILE); + +	/* We will be updating the cookie too. */ +	BUG_ON(!cookie->def->get_aux); + +	/* If there's an object, we tell the object state machine to handle the +	 * invalidation on our behalf, otherwise there's nothing to do. +	 */ +	if (!hlist_empty(&cookie->backing_objects)) { +		spin_lock(&cookie->lock); + +		if (fscache_cookie_enabled(cookie) && +		    !hlist_empty(&cookie->backing_objects) && +		    !test_and_set_bit(FSCACHE_COOKIE_INVALIDATING, +				      &cookie->flags)) { +			object = hlist_entry(cookie->backing_objects.first, +					     struct fscache_object, +					     cookie_link); +			if (fscache_object_is_live(object)) +				fscache_raise_event( +					object, FSCACHE_OBJECT_EV_INVALIDATE); +		} + +		spin_unlock(&cookie->lock); +	} + +	_leave(""); +} +EXPORT_SYMBOL(__fscache_invalidate); + +/* + * Wait for object invalidation to complete. + */ +void __fscache_wait_on_invalidate(struct fscache_cookie *cookie) +{ +	_enter("%p", cookie); + +	wait_on_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING, +		    fscache_wait_bit_interruptible, +		    TASK_UNINTERRUPTIBLE); + +	_leave(""); +} +EXPORT_SYMBOL(__fscache_wait_on_invalidate); + +/*   * update the index entries backing a cookie   */  void __fscache_update_cookie(struct fscache_cookie *cookie)  {  	struct fscache_object *object; -	struct hlist_node *_p;  	fscache_stat(&fscache_n_updates); @@ -391,10 +491,14 @@ void __fscache_update_cookie(struct fscache_cookie *cookie)  	spin_lock(&cookie->lock); -	/* update the index entry on disk in each cache backing this cookie */ -	hlist_for_each_entry(object, _p, -			     &cookie->backing_objects, cookie_link) { -		fscache_raise_event(object, FSCACHE_OBJECT_EV_UPDATE); +	if (fscache_cookie_enabled(cookie)) { +		/* update the index entry on disk in each cache backing this +		 * cookie. +		 */ +		hlist_for_each_entry(object, +				     &cookie->backing_objects, cookie_link) { +			fscache_raise_event(object, FSCACHE_OBJECT_EV_UPDATE); +		}  	}  	spin_unlock(&cookie->lock); @@ -403,74 +507,103 @@ void __fscache_update_cookie(struct fscache_cookie *cookie)  EXPORT_SYMBOL(__fscache_update_cookie);  /* - * release a cookie back to the cache - * - the object will be marked as recyclable on disk if retire is true - * - all dependents of this cookie must have already been unregistered - *   (indices/files/pages) + * Disable a cookie to stop it from accepting new requests from the netfs.   */ -void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire) +void __fscache_disable_cookie(struct fscache_cookie *cookie, bool invalidate)  { -	struct fscache_cache *cache;  	struct fscache_object *object; -	unsigned long event; - -	fscache_stat(&fscache_n_relinquishes); -	if (retire) -		fscache_stat(&fscache_n_relinquishes_retire); +	bool awaken = false; -	if (!cookie) { -		fscache_stat(&fscache_n_relinquishes_null); -		_leave(" [no cookie]"); -		return; -	} +	_enter("%p,%u", cookie, invalidate); -	_enter("%p{%s,%p},%d", -	       cookie, cookie->def->name, cookie->netfs_data, retire); +	ASSERTCMP(atomic_read(&cookie->n_active), >, 0);  	if (atomic_read(&cookie->n_children) != 0) { -		printk(KERN_ERR "FS-Cache: Cookie '%s' still has children\n", +		pr_err("Cookie '%s' still has children\n",  		       cookie->def->name);  		BUG();  	} -	/* wait for the cookie to finish being instantiated (or to fail) */ -	if (test_bit(FSCACHE_COOKIE_CREATING, &cookie->flags)) { -		fscache_stat(&fscache_n_relinquishes_waitcrt); -		wait_on_bit(&cookie->flags, FSCACHE_COOKIE_CREATING, -			    fscache_wait_bit, TASK_UNINTERRUPTIBLE); -	} +	wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK, +			 fscache_wait_bit, TASK_UNINTERRUPTIBLE); +	if (!test_and_clear_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags)) +		goto out_unlock_enable; + +	/* If the cookie is being invalidated, wait for that to complete first +	 * so that we can reuse the flag. +	 */ +	__fscache_wait_on_invalidate(cookie); -	event = retire ? FSCACHE_OBJECT_EV_RETIRE : FSCACHE_OBJECT_EV_RELEASE; +	/* Dispose of the backing objects */ +	set_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags);  	spin_lock(&cookie->lock); +	if (!hlist_empty(&cookie->backing_objects)) { +		hlist_for_each_entry(object, &cookie->backing_objects, cookie_link) { +			if (invalidate) +				set_bit(FSCACHE_OBJECT_RETIRED, &object->flags); +			fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); +		} +	} else { +		if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) +			awaken = true; +	} +	spin_unlock(&cookie->lock); +	if (awaken) +		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); + +	/* Wait for cessation of activity requiring access to the netfs (when +	 * n_active reaches 0).  This makes sure outstanding reads and writes +	 * have completed. +	 */ +	if (!atomic_dec_and_test(&cookie->n_active)) +		wait_on_atomic_t(&cookie->n_active, fscache_wait_atomic_t, +				 TASK_UNINTERRUPTIBLE); + +	/* Reset the cookie state if it wasn't relinquished */ +	if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags)) { +		atomic_inc(&cookie->n_active); +		set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); +	} -	/* break links with all the active objects */ -	while (!hlist_empty(&cookie->backing_objects)) { -		object = hlist_entry(cookie->backing_objects.first, -				     struct fscache_object, -				     cookie_link); +out_unlock_enable: +	clear_bit_unlock(FSCACHE_COOKIE_ENABLEMENT_LOCK, &cookie->flags); +	wake_up_bit(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK); +	_leave(""); +} +EXPORT_SYMBOL(__fscache_disable_cookie); -		_debug("RELEASE OBJ%x", object->debug_id); +/* + * release a cookie back to the cache + * - the object will be marked as recyclable on disk if retire is true + * - all dependents of this cookie must have already been unregistered + *   (indices/files/pages) + */ +void __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire) +{ +	fscache_stat(&fscache_n_relinquishes); +	if (retire) +		fscache_stat(&fscache_n_relinquishes_retire); + +	if (!cookie) { +		fscache_stat(&fscache_n_relinquishes_null); +		_leave(" [no cookie]"); +		return; +	} -		/* detach each cache object from the object cookie */ -		spin_lock(&object->lock); -		hlist_del_init(&object->cookie_link); +	_enter("%p{%s,%p,%d},%d", +	       cookie, cookie->def->name, cookie->netfs_data, +	       atomic_read(&cookie->n_active), retire); -		cache = object->cache; -		object->cookie = NULL; -		fscache_raise_event(object, event); -		spin_unlock(&object->lock); +	/* No further netfs-accessing operations on this cookie permitted */ +	set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags); -		if (atomic_dec_and_test(&cookie->usage)) -			/* the cookie refcount shouldn't be reduced to 0 yet */ -			BUG(); -	} +	__fscache_disable_cookie(cookie, retire); -	/* detach pointers back to the netfs */ +	/* Clear pointers back to the netfs */  	cookie->netfs_data	= NULL;  	cookie->def		= NULL; - -	spin_unlock(&cookie->lock); +	BUG_ON(cookie->stores.rnode);  	if (cookie->parent) {  		ASSERTCMP(atomic_read(&cookie->parent->usage), >, 0); @@ -478,7 +611,7 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire)  		atomic_dec(&cookie->parent->n_children);  	} -	/* finally dispose of the cookie */ +	/* Dispose of the netfs's link to the cookie */  	ASSERTCMP(atomic_read(&cookie->usage), >, 0);  	fscache_cookie_put(cookie); @@ -512,3 +645,79 @@ void __fscache_cookie_put(struct fscache_cookie *cookie)  	_leave("");  } + +/* + * check the consistency between the netfs inode and the backing cache + * + * NOTE: it only serves no-index type + */ +int __fscache_check_consistency(struct fscache_cookie *cookie) +{ +	struct fscache_operation *op; +	struct fscache_object *object; +	bool wake_cookie = false; +	int ret; + +	_enter("%p,", cookie); + +	ASSERTCMP(cookie->def->type, ==, FSCACHE_COOKIE_TYPE_DATAFILE); + +	if (fscache_wait_for_deferred_lookup(cookie) < 0) +		return -ERESTARTSYS; + +	if (hlist_empty(&cookie->backing_objects)) +		return 0; + +	op = kzalloc(sizeof(*op), GFP_NOIO | __GFP_NOMEMALLOC | __GFP_NORETRY); +	if (!op) +		return -ENOMEM; + +	fscache_operation_init(op, NULL, NULL); +	op->flags = FSCACHE_OP_MYTHREAD | +		(1 << FSCACHE_OP_WAITING) | +		(1 << FSCACHE_OP_UNUSE_COOKIE); + +	spin_lock(&cookie->lock); + +	if (!fscache_cookie_enabled(cookie) || +	    hlist_empty(&cookie->backing_objects)) +		goto inconsistent; +	object = hlist_entry(cookie->backing_objects.first, +			     struct fscache_object, cookie_link); +	if (test_bit(FSCACHE_IOERROR, &object->cache->flags)) +		goto inconsistent; + +	op->debug_id = atomic_inc_return(&fscache_op_debug_id); + +	__fscache_use_cookie(cookie); +	if (fscache_submit_op(object, op) < 0) +		goto submit_failed; + +	/* the work queue now carries its own ref on the object */ +	spin_unlock(&cookie->lock); + +	ret = fscache_wait_for_operation_activation(object, op, +						    NULL, NULL, NULL); +	if (ret == 0) { +		/* ask the cache to honour the operation */ +		ret = object->cache->ops->check_consistency(op); +		fscache_op_complete(op, false); +	} else if (ret == -ENOBUFS) { +		ret = 0; +	} + +	fscache_put_operation(op); +	_leave(" = %d", ret); +	return ret; + +submit_failed: +	wake_cookie = __fscache_unuse_cookie(cookie); +inconsistent: +	spin_unlock(&cookie->lock); +	if (wake_cookie) +		__fscache_wake_unused_cookie(cookie); +	kfree(op); +	_leave(" = -ESTALE"); +	return -ESTALE; +} +EXPORT_SYMBOL(__fscache_check_consistency); diff --git a/fs/fscache/fsdef.c b/fs/fscache/fsdef.c index f5b4baee735..5a117df2a9e 100644 --- a/fs/fscache/fsdef.c +++ b/fs/fscache/fsdef.c @@ -55,9 +55,11 @@ static struct fscache_cookie_def fscache_fsdef_index_def = {  struct fscache_cookie fscache_fsdef_index = {  	.usage		= ATOMIC_INIT(1), +	.n_active	= ATOMIC_INIT(1),  	.lock		= __SPIN_LOCK_UNLOCKED(fscache_fsdef_index.lock),  	.backing_objects = HLIST_HEAD_INIT,  	.def		= &fscache_fsdef_index_def, +	.flags		= 1 << FSCACHE_COOKIE_ENABLED,  };  EXPORT_SYMBOL(fscache_fsdef_index); diff --git a/fs/fscache/histogram.c b/fs/fscache/histogram.c index bad496748a5..7d637e2335f 100644 --- a/fs/fscache/histogram.c +++ b/fs/fscache/histogram.c @@ -31,12 +31,10 @@ static int fscache_histogram_show(struct seq_file *m, void *v)  	switch ((unsigned long) v) {  	case 1: -		seq_puts(m, "JIFS  SECS  OBJ INST  OP RUNS   OBJ RUNS " -			 " RETRV DLY RETRIEVLS\n"); +		seq_puts(m, "JIFS  SECS  OBJ INST  OP RUNS   OBJ RUNS  RETRV DLY RETRIEVLS\n");  		return 0;  	case 2: -		seq_puts(m, "===== ===== ========= ========= =========" -			 " ========= =========\n"); +		seq_puts(m, "===== ===== ========= ========= ========= ========= =========\n");  		return 0;  	default:  		index = (unsigned long) v - 3; diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index f6aad48d38a..bc6c08fcfdd 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -22,6 +22,12 @@   *   */ +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) "FS-Cache: " fmt +  #include <linux/fscache-cache.h>  #include <linux/sched.h> @@ -93,14 +99,11 @@ static inline bool fscache_object_congested(void)  extern int fscache_wait_bit(void *);  extern int fscache_wait_bit_interruptible(void *); +extern int fscache_wait_atomic_t(atomic_t *);  /*   * object.c   */ -extern const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5]; - -extern void fscache_withdrawing_object(struct fscache_cache *, -				       struct fscache_object *);  extern void fscache_enqueue_object(struct fscache_object *);  /* @@ -110,8 +113,10 @@ extern void fscache_enqueue_object(struct fscache_object *);  extern const struct file_operations fscache_objlist_fops;  extern void fscache_objlist_add(struct fscache_object *); +extern void fscache_objlist_remove(struct fscache_object *);  #else  #define fscache_objlist_add(object) do {} while(0) +#define fscache_objlist_remove(object) do {} while(0)  #endif  /* @@ -121,12 +126,25 @@ extern int fscache_submit_exclusive_op(struct fscache_object *,  				       struct fscache_operation *);  extern int fscache_submit_op(struct fscache_object *,  			     struct fscache_operation *); -extern int fscache_cancel_op(struct fscache_operation *); +extern int fscache_cancel_op(struct fscache_operation *, +			     void (*)(struct fscache_operation *)); +extern void fscache_cancel_all_ops(struct fscache_object *);  extern void fscache_abort_object(struct fscache_object *);  extern void fscache_start_operations(struct fscache_object *);  extern void fscache_operation_gc(struct work_struct *);  /* + * page.c + */ +extern int fscache_wait_for_deferred_lookup(struct fscache_cookie *); +extern int fscache_wait_for_operation_activation(struct fscache_object *, +						 struct fscache_operation *, +						 atomic_t *, +						 atomic_t *, +						 void (*)(struct fscache_operation *)); +extern void fscache_invalidate_writes(struct fscache_cookie *); + +/*   * proc.c   */  #ifdef CONFIG_PROC_FS @@ -194,6 +212,7 @@ extern atomic_t fscache_n_store_vmscan_not_storing;  extern atomic_t fscache_n_store_vmscan_gone;  extern atomic_t fscache_n_store_vmscan_busy;  extern atomic_t fscache_n_store_vmscan_cancelled; +extern atomic_t fscache_n_store_vmscan_wait;  extern atomic_t fscache_n_marks;  extern atomic_t fscache_n_uncaches; @@ -205,6 +224,9 @@ extern atomic_t fscache_n_acquires_ok;  extern atomic_t fscache_n_acquires_nobufs;  extern atomic_t fscache_n_acquires_oom; +extern atomic_t fscache_n_invalidates; +extern atomic_t fscache_n_invalidates_run; +  extern atomic_t fscache_n_updates;  extern atomic_t fscache_n_updates_null;  extern atomic_t fscache_n_updates_run; @@ -237,6 +259,7 @@ extern atomic_t fscache_n_cop_alloc_object;  extern atomic_t fscache_n_cop_lookup_object;  extern atomic_t fscache_n_cop_lookup_complete;  extern atomic_t fscache_n_cop_grab_object; +extern atomic_t fscache_n_cop_invalidate_object;  extern atomic_t fscache_n_cop_update_object;  extern atomic_t fscache_n_cop_drop_object;  extern atomic_t fscache_n_cop_put_object; @@ -278,6 +301,11 @@ extern const struct file_operations fscache_stats_fops;  static inline void fscache_raise_event(struct fscache_object *object,  				       unsigned event)  { +	BUG_ON(event >= NR_FSCACHE_OBJECT_EVENTS); +#if 0 +	printk("*** fscache_raise_event(OBJ%d{%lx},%x)\n", +	       object->debug_id, object->event_mask, (1 << event)); +#endif  	if (!test_and_set_bit(event, &object->events) &&  	    test_bit(event, &object->event_mask))  		fscache_enqueue_object(object); @@ -391,8 +419,8 @@ do {						\  #define ASSERT(X)							\  do {									\  	if (unlikely(!(X))) {						\ -		printk(KERN_ERR "\n");					\ -		printk(KERN_ERR "FS-Cache: Assertion failed\n");	\ +		pr_err("\n");					\ +		pr_err("Assertion failed\n");	\  		BUG();							\  	}								\  } while (0) @@ -400,9 +428,9 @@ do {									\  #define ASSERTCMP(X, OP, Y)						\  do {									\  	if (unlikely(!((X) OP (Y)))) {					\ -		printk(KERN_ERR "\n");					\ -		printk(KERN_ERR "FS-Cache: Assertion failed\n");	\ -		printk(KERN_ERR "%lx " #OP " %lx is false\n",		\ +		pr_err("\n");					\ +		pr_err("Assertion failed\n");	\ +		pr_err("%lx " #OP " %lx is false\n",		\  		       (unsigned long)(X), (unsigned long)(Y));		\  		BUG();							\  	}								\ @@ -411,8 +439,8 @@ do {									\  #define ASSERTIF(C, X)							\  do {									\  	if (unlikely((C) && !(X))) {					\ -		printk(KERN_ERR "\n");					\ -		printk(KERN_ERR "FS-Cache: Assertion failed\n");	\ +		pr_err("\n");					\ +		pr_err("Assertion failed\n");	\  		BUG();							\  	}								\  } while (0) @@ -420,9 +448,9 @@ do {									\  #define ASSERTIFCMP(C, X, OP, Y)					\  do {									\  	if (unlikely((C) && !((X) OP (Y)))) {				\ -		printk(KERN_ERR "\n");					\ -		printk(KERN_ERR "FS-Cache: Assertion failed\n");	\ -		printk(KERN_ERR "%lx " #OP " %lx is false\n",		\ +		pr_err("\n");					\ +		pr_err("Assertion failed\n");	\ +		pr_err("%lx " #OP " %lx is false\n",		\  		       (unsigned long)(X), (unsigned long)(Y));		\  		BUG();							\  	}								\ diff --git a/fs/fscache/main.c b/fs/fscache/main.c index f9d856773f7..63f868e869b 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -67,7 +67,7 @@ static int fscache_max_active_sysctl(struct ctl_table *table, int write,  	return ret;  } -ctl_table fscache_sysctls[] = { +struct ctl_table fscache_sysctls[] = {  	{  		.procname	= "object_max_active",  		.data		= &fscache_object_max_active, @@ -87,7 +87,7 @@ ctl_table fscache_sysctls[] = {  	{}  }; -ctl_table fscache_sysctls_root[] = { +struct ctl_table fscache_sysctls_root[] = {  	{  		.procname	= "fscache",  		.mode		= 0555, @@ -146,8 +146,7 @@ static int __init fscache_init(void)  					       0,  					       fscache_cookie_init_once);  	if (!fscache_cookie_jar) { -		printk(KERN_NOTICE -		       "FS-Cache: Failed to allocate a cookie jar\n"); +		pr_notice("Failed to allocate a cookie jar\n");  		ret = -ENOMEM;  		goto error_cookie_jar;  	} @@ -156,7 +155,7 @@ static int __init fscache_init(void)  	if (!fscache_root)  		goto error_kobj; -	printk(KERN_NOTICE "FS-Cache: Loaded\n"); +	pr_notice("Loaded\n");  	return 0;  error_kobj: @@ -192,7 +191,7 @@ static void __exit fscache_exit(void)  	fscache_proc_cleanup();  	destroy_workqueue(fscache_op_wq);  	destroy_workqueue(fscache_object_wq); -	printk(KERN_NOTICE "FS-Cache: Unloaded\n"); +	pr_notice("Unloaded\n");  }  module_exit(fscache_exit); @@ -205,7 +204,6 @@ int fscache_wait_bit(void *flags)  	schedule();  	return 0;  } -EXPORT_SYMBOL(fscache_wait_bit);  /*   * wait_on_bit() sleep function for interruptible waiting @@ -215,4 +213,12 @@ int fscache_wait_bit_interruptible(void *flags)  	schedule();  	return signal_pending(current);  } -EXPORT_SYMBOL(fscache_wait_bit_interruptible); + +/* + * wait_on_atomic_t() sleep function for uninterruptible waiting + */ +int fscache_wait_atomic_t(atomic_t *p) +{ +	schedule(); +	return 0; +} diff --git a/fs/fscache/netfs.c b/fs/fscache/netfs.c index e028b8eb1c4..6d941f56faf 100644 --- a/fs/fscache/netfs.c +++ b/fs/fscache/netfs.c @@ -40,10 +40,12 @@ int __fscache_register_netfs(struct fscache_netfs *netfs)  	/* initialise the primary index cookie */  	atomic_set(&netfs->primary_index->usage, 1);  	atomic_set(&netfs->primary_index->n_children, 0); +	atomic_set(&netfs->primary_index->n_active, 1);  	netfs->primary_index->def		= &fscache_fsdef_netfs_def;  	netfs->primary_index->parent		= &fscache_fsdef_index;  	netfs->primary_index->netfs_data	= netfs; +	netfs->primary_index->flags		= 1 << FSCACHE_COOKIE_ENABLED;  	atomic_inc(&netfs->primary_index->parent->usage);  	atomic_inc(&netfs->primary_index->parent->n_children); @@ -63,8 +65,7 @@ int __fscache_register_netfs(struct fscache_netfs *netfs)  	list_add(&netfs->link, &fscache_netfs_list);  	ret = 0; -	printk(KERN_NOTICE "FS-Cache: Netfs '%s' registered for caching\n", -	       netfs->name); +	pr_notice("Netfs '%s' registered for caching\n", netfs->name);  already_registered:  	up_write(&fscache_addremove_sem); @@ -95,8 +96,8 @@ void __fscache_unregister_netfs(struct fscache_netfs *netfs)  	up_write(&fscache_addremove_sem); -	printk(KERN_NOTICE "FS-Cache: Netfs '%s' unregistered from caching\n", -	       netfs->name); +	pr_notice("Netfs '%s' unregistered from caching\n", +		  netfs->name);  	_leave("");  } diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c index ebe29c58138..b8179ca6bf9 100644 --- a/fs/fscache/object-list.c +++ b/fs/fscache/object-list.c @@ -50,6 +50,8 @@ void fscache_objlist_add(struct fscache_object *obj)  	struct fscache_object *xobj;  	struct rb_node **p = &fscache_object_list.rb_node, *parent = NULL; +	ASSERT(RB_EMPTY_NODE(&obj->objlist_link)); +  	write_lock(&fscache_object_list_lock);  	while (*p) { @@ -70,14 +72,14 @@ void fscache_objlist_add(struct fscache_object *obj)  	write_unlock(&fscache_object_list_lock);  } -/** - * fscache_object_destroy - Note that a cache object is about to be destroyed - * @object: The object to be destroyed - * - * Note the imminent destruction and deallocation of a cache object record. +/* + * Remove an object from the object list.   */ -void fscache_object_destroy(struct fscache_object *obj) +void fscache_objlist_remove(struct fscache_object *obj)  { +	if (RB_EMPTY_NODE(&obj->objlist_link)) +		return; +  	write_lock(&fscache_object_list_lock);  	BUG_ON(RB_EMPTY_ROOT(&fscache_object_list)); @@ -85,7 +87,6 @@ void fscache_object_destroy(struct fscache_object *obj)  	write_unlock(&fscache_object_list_lock);  } -EXPORT_SYMBOL(fscache_object_destroy);  /*   * find the object in the tree on or after the specified index @@ -166,15 +167,14 @@ static int fscache_objlist_show(struct seq_file *m, void *v)  {  	struct fscache_objlist_data *data = m->private;  	struct fscache_object *obj = v; +	struct fscache_cookie *cookie;  	unsigned long config = data->config; -	uint16_t keylen, auxlen;  	char _type[3], *type; -	bool no_cookie;  	u8 *buf = data->buf, *p;  	if ((unsigned long) v == 1) {  		seq_puts(m, "OBJECT   PARENT   STAT CHLDN OPS OOP IPR EX READS" -			 " EM EV F S" +			 " EM EV FL S"  			 " | NETFS_COOKIE_DEF TY FL NETFS_DATA");  		if (config & (FSCACHE_OBJLIST_CONFIG_KEY |  			      FSCACHE_OBJLIST_CONFIG_AUX)) @@ -193,7 +193,7 @@ static int fscache_objlist_show(struct seq_file *m, void *v)  	if ((unsigned long) v == 2) {  		seq_puts(m, "======== ======== ==== ===== === === === == =====" -			 " == == = =" +			 " == == == ="  			 " | ================ == == ================");  		if (config & (FSCACHE_OBJLIST_CONFIG_KEY |  			      FSCACHE_OBJLIST_CONFIG_AUX)) @@ -216,10 +216,11 @@ static int fscache_objlist_show(struct seq_file *m, void *v)  		}							\  	} while(0) +	cookie = obj->cookie;  	if (~config) { -		FILTER(obj->cookie, +		FILTER(cookie->def,  		       COOKIE, NOCOOKIE); -		FILTER(obj->state != FSCACHE_OBJECT_ACTIVE || +		FILTER(fscache_object_is_active(obj) ||  		       obj->n_ops != 0 ||  		       obj->n_obj_ops != 0 ||  		       obj->flags || @@ -235,79 +236,70 @@ static int fscache_objlist_show(struct seq_file *m, void *v)  	}  	seq_printf(m, -		   "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1x | ", +		   "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %2lx %1x | ",  		   obj->debug_id,  		   obj->parent ? obj->parent->debug_id : -1, -		   fscache_object_states_short[obj->state], +		   obj->state->short_name,  		   obj->n_children,  		   obj->n_ops,  		   obj->n_obj_ops,  		   obj->n_in_progress,  		   obj->n_exclusive,  		   atomic_read(&obj->n_reads), -		   obj->event_mask & FSCACHE_OBJECT_EVENTS_MASK, +		   obj->event_mask,  		   obj->events,  		   obj->flags,  		   work_busy(&obj->work)); -	no_cookie = true; -	keylen = auxlen = 0; -	if (obj->cookie) { -		spin_lock(&obj->lock); -		if (obj->cookie) { -			switch (obj->cookie->def->type) { -			case 0: -				type = "IX"; -				break; -			case 1: -				type = "DT"; -				break; -			default: -				sprintf(_type, "%02u", -					obj->cookie->def->type); -				type = _type; -				break; -			} +	if (fscache_use_cookie(obj)) { +		uint16_t keylen = 0, auxlen = 0; -			seq_printf(m, "%-16s %s %2lx %16p", -				   obj->cookie->def->name, -				   type, -				   obj->cookie->flags, -				   obj->cookie->netfs_data); - -			if (obj->cookie->def->get_key && -			    config & FSCACHE_OBJLIST_CONFIG_KEY) -				keylen = obj->cookie->def->get_key( -					obj->cookie->netfs_data, -					buf, 400); - -			if (obj->cookie->def->get_aux && -			    config & FSCACHE_OBJLIST_CONFIG_AUX) -				auxlen = obj->cookie->def->get_aux( -					obj->cookie->netfs_data, -					buf + keylen, 512 - keylen); - -			no_cookie = false; +		switch (cookie->def->type) { +		case 0: +			type = "IX"; +			break; +		case 1: +			type = "DT"; +			break; +		default: +			sprintf(_type, "%02u", cookie->def->type); +			type = _type; +			break;  		} -		spin_unlock(&obj->lock); -		if (!no_cookie && (keylen > 0 || auxlen > 0)) { -			seq_printf(m, " "); +		seq_printf(m, "%-16s %s %2lx %16p", +			   cookie->def->name, +			   type, +			   cookie->flags, +			   cookie->netfs_data); + +		if (cookie->def->get_key && +		    config & FSCACHE_OBJLIST_CONFIG_KEY) +			keylen = cookie->def->get_key(cookie->netfs_data, +						      buf, 400); + +		if (cookie->def->get_aux && +		    config & FSCACHE_OBJLIST_CONFIG_AUX) +			auxlen = cookie->def->get_aux(cookie->netfs_data, +						      buf + keylen, 512 - keylen); +		fscache_unuse_cookie(obj); + +		if (keylen > 0 || auxlen > 0) { +			seq_puts(m, " ");  			for (p = buf; keylen > 0; keylen--)  				seq_printf(m, "%02x", *p++);  			if (auxlen > 0) {  				if (config & FSCACHE_OBJLIST_CONFIG_KEY) -					seq_printf(m, ", "); +					seq_puts(m, ", ");  				for (; auxlen > 0; auxlen--)  					seq_printf(m, "%02x", *p++);  			}  		} -	} -	if (no_cookie) -		seq_printf(m, "<no_cookie>\n"); -	else -		seq_printf(m, "\n"); +		seq_puts(m, "\n"); +	} else { +		seq_puts(m, "<no_netfs>\n"); +	}  	return 0;  } diff --git a/fs/fscache/object.c b/fs/fscache/object.c index b6b897c550a..d3b4539f165 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c @@ -14,49 +14,132 @@  #define FSCACHE_DEBUG_LEVEL COOKIE  #include <linux/module.h> +#include <linux/slab.h> +#include <linux/prefetch.h>  #include "internal.h" -const char *fscache_object_states[FSCACHE_OBJECT__NSTATES] = { -	[FSCACHE_OBJECT_INIT]		= "OBJECT_INIT", -	[FSCACHE_OBJECT_LOOKING_UP]	= "OBJECT_LOOKING_UP", -	[FSCACHE_OBJECT_CREATING]	= "OBJECT_CREATING", -	[FSCACHE_OBJECT_AVAILABLE]	= "OBJECT_AVAILABLE", -	[FSCACHE_OBJECT_ACTIVE]		= "OBJECT_ACTIVE", -	[FSCACHE_OBJECT_UPDATING]	= "OBJECT_UPDATING", -	[FSCACHE_OBJECT_DYING]		= "OBJECT_DYING", -	[FSCACHE_OBJECT_LC_DYING]	= "OBJECT_LC_DYING", -	[FSCACHE_OBJECT_ABORT_INIT]	= "OBJECT_ABORT_INIT", -	[FSCACHE_OBJECT_RELEASING]	= "OBJECT_RELEASING", -	[FSCACHE_OBJECT_RECYCLING]	= "OBJECT_RECYCLING", -	[FSCACHE_OBJECT_WITHDRAWING]	= "OBJECT_WITHDRAWING", -	[FSCACHE_OBJECT_DEAD]		= "OBJECT_DEAD", +static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *, int); +static const struct fscache_state *fscache_kill_dependents(struct fscache_object *, int); +static const struct fscache_state *fscache_drop_object(struct fscache_object *, int); +static const struct fscache_state *fscache_initialise_object(struct fscache_object *, int); +static const struct fscache_state *fscache_invalidate_object(struct fscache_object *, int); +static const struct fscache_state *fscache_jumpstart_dependents(struct fscache_object *, int); +static const struct fscache_state *fscache_kill_object(struct fscache_object *, int); +static const struct fscache_state *fscache_lookup_failure(struct fscache_object *, int); +static const struct fscache_state *fscache_look_up_object(struct fscache_object *, int); +static const struct fscache_state *fscache_object_available(struct fscache_object *, int); +static const struct fscache_state *fscache_parent_ready(struct fscache_object *, int); +static const struct fscache_state *fscache_update_object(struct fscache_object *, int); + +#define __STATE_NAME(n) fscache_osm_##n +#define STATE(n) (&__STATE_NAME(n)) + +/* + * Define a work state.  Work states are execution states.  No event processing + * is performed by them.  The function attached to a work state returns a + * pointer indicating the next state to which the state machine should + * transition.  Returning NO_TRANSIT repeats the current state, but goes back + * to the scheduler first. + */ +#define WORK_STATE(n, sn, f) \ +	const struct fscache_state __STATE_NAME(n) = {			\ +		.name = #n,						\ +		.short_name = sn,					\ +		.work = f						\ +	} + +/* + * Returns from work states. + */ +#define transit_to(state) ({ prefetch(&STATE(state)->work); STATE(state); }) + +#define NO_TRANSIT ((struct fscache_state *)NULL) + +/* + * Define a wait state.  Wait states are event processing states.  No execution + * is performed by them.  Wait states are just tables of "if event X occurs, + * clear it and transition to state Y".  The dispatcher returns to the + * scheduler if none of the events in which the wait state has an interest are + * currently pending. + */ +#define WAIT_STATE(n, sn, ...) \ +	const struct fscache_state __STATE_NAME(n) = {			\ +		.name = #n,						\ +		.short_name = sn,					\ +		.work = NULL,						\ +		.transitions = { __VA_ARGS__, { 0, NULL } }		\ +	} + +#define TRANSIT_TO(state, emask) \ +	{ .events = (emask), .transit_to = STATE(state) } + +/* + * The object state machine. + */ +static WORK_STATE(INIT_OBJECT,		"INIT", fscache_initialise_object); +static WORK_STATE(PARENT_READY,		"PRDY", fscache_parent_ready); +static WORK_STATE(ABORT_INIT,		"ABRT", fscache_abort_initialisation); +static WORK_STATE(LOOK_UP_OBJECT,	"LOOK", fscache_look_up_object); +static WORK_STATE(CREATE_OBJECT,	"CRTO", fscache_look_up_object); +static WORK_STATE(OBJECT_AVAILABLE,	"AVBL", fscache_object_available); +static WORK_STATE(JUMPSTART_DEPS,	"JUMP", fscache_jumpstart_dependents); + +static WORK_STATE(INVALIDATE_OBJECT,	"INVL", fscache_invalidate_object); +static WORK_STATE(UPDATE_OBJECT,	"UPDT", fscache_update_object); + +static WORK_STATE(LOOKUP_FAILURE,	"LCFL", fscache_lookup_failure); +static WORK_STATE(KILL_OBJECT,		"KILL", fscache_kill_object); +static WORK_STATE(KILL_DEPENDENTS,	"KDEP", fscache_kill_dependents); +static WORK_STATE(DROP_OBJECT,		"DROP", fscache_drop_object); +static WORK_STATE(OBJECT_DEAD,		"DEAD", (void*)2UL); + +static WAIT_STATE(WAIT_FOR_INIT,	"?INI", +		  TRANSIT_TO(INIT_OBJECT,	1 << FSCACHE_OBJECT_EV_NEW_CHILD)); + +static WAIT_STATE(WAIT_FOR_PARENT,	"?PRN", +		  TRANSIT_TO(PARENT_READY,	1 << FSCACHE_OBJECT_EV_PARENT_READY)); + +static WAIT_STATE(WAIT_FOR_CMD,		"?CMD", +		  TRANSIT_TO(INVALIDATE_OBJECT,	1 << FSCACHE_OBJECT_EV_INVALIDATE), +		  TRANSIT_TO(UPDATE_OBJECT,	1 << FSCACHE_OBJECT_EV_UPDATE), +		  TRANSIT_TO(JUMPSTART_DEPS,	1 << FSCACHE_OBJECT_EV_NEW_CHILD)); + +static WAIT_STATE(WAIT_FOR_CLEARANCE,	"?CLR", +		  TRANSIT_TO(KILL_OBJECT,	1 << FSCACHE_OBJECT_EV_CLEARED)); + +/* + * Out-of-band event transition tables.  These are for handling unexpected + * events, such as an I/O error.  If an OOB event occurs, the state machine + * clears and disables the event and forces a transition to the nominated work + * state (acurrently executing work states will complete first). + * + * In such a situation, object->state remembers the state the machine should + * have been in/gone to and returning NO_TRANSIT returns to that. + */ +static const struct fscache_transition fscache_osm_init_oob[] = { +	   TRANSIT_TO(ABORT_INIT, +		      (1 << FSCACHE_OBJECT_EV_ERROR) | +		      (1 << FSCACHE_OBJECT_EV_KILL)), +	   { 0, NULL } +}; + +static const struct fscache_transition fscache_osm_lookup_oob[] = { +	   TRANSIT_TO(LOOKUP_FAILURE, +		      (1 << FSCACHE_OBJECT_EV_ERROR) | +		      (1 << FSCACHE_OBJECT_EV_KILL)), +	   { 0, NULL }  }; -EXPORT_SYMBOL(fscache_object_states); - -const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = { -	[FSCACHE_OBJECT_INIT]		= "INIT", -	[FSCACHE_OBJECT_LOOKING_UP]	= "LOOK", -	[FSCACHE_OBJECT_CREATING]	= "CRTN", -	[FSCACHE_OBJECT_AVAILABLE]	= "AVBL", -	[FSCACHE_OBJECT_ACTIVE]		= "ACTV", -	[FSCACHE_OBJECT_UPDATING]	= "UPDT", -	[FSCACHE_OBJECT_DYING]		= "DYNG", -	[FSCACHE_OBJECT_LC_DYING]	= "LCDY", -	[FSCACHE_OBJECT_ABORT_INIT]	= "ABTI", -	[FSCACHE_OBJECT_RELEASING]	= "RELS", -	[FSCACHE_OBJECT_RECYCLING]	= "RCYC", -	[FSCACHE_OBJECT_WITHDRAWING]	= "WTHD", -	[FSCACHE_OBJECT_DEAD]		= "DEAD", + +static const struct fscache_transition fscache_osm_run_oob[] = { +	   TRANSIT_TO(KILL_OBJECT, +		      (1 << FSCACHE_OBJECT_EV_ERROR) | +		      (1 << FSCACHE_OBJECT_EV_KILL)), +	   { 0, NULL }  };  static int  fscache_get_object(struct fscache_object *);  static void fscache_put_object(struct fscache_object *); -static void fscache_initialise_object(struct fscache_object *); -static void fscache_lookup_object(struct fscache_object *); -static void fscache_object_available(struct fscache_object *); -static void fscache_release_object(struct fscache_object *); -static void fscache_withdraw_object(struct fscache_object *); -static void fscache_enqueue_dependents(struct fscache_object *); +static bool fscache_enqueue_dependents(struct fscache_object *, int);  static void fscache_dequeue_object(struct fscache_object *);  /* @@ -71,265 +154,116 @@ static inline void fscache_done_parent_op(struct fscache_object *object)  	       object->debug_id, parent->debug_id, parent->n_ops);  	spin_lock_nested(&parent->lock, 1); -	parent->n_ops--;  	parent->n_obj_ops--; +	parent->n_ops--;  	if (parent->n_ops == 0)  		fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED);  	spin_unlock(&parent->lock);  }  /* - * process events that have been sent to an object's state machine - * - initiates parent lookup - * - does object lookup - * - does object creation - * - does object recycling and retirement - * - does object withdrawal - */ -static void fscache_object_state_machine(struct fscache_object *object) + * Object state machine dispatcher. + */ +static void fscache_object_sm_dispatcher(struct fscache_object *object)  { -	enum fscache_object_state new_state; -	struct fscache_cookie *cookie; +	const struct fscache_transition *t; +	const struct fscache_state *state, *new_state; +	unsigned long events, event_mask; +	int event = -1;  	ASSERT(object != NULL);  	_enter("{OBJ%x,%s,%lx}", -	       object->debug_id, fscache_object_states[object->state], -	       object->events); - -	switch (object->state) { -		/* wait for the parent object to become ready */ -	case FSCACHE_OBJECT_INIT: -		object->event_mask = -			ULONG_MAX & ~(1 << FSCACHE_OBJECT_EV_CLEARED); -		fscache_initialise_object(object); -		goto done; - -		/* look up the object metadata on disk */ -	case FSCACHE_OBJECT_LOOKING_UP: -		fscache_lookup_object(object); -		goto lookup_transit; - -		/* create the object metadata on disk */ -	case FSCACHE_OBJECT_CREATING: -		fscache_lookup_object(object); -		goto lookup_transit; - -		/* handle an object becoming available; start pending -		 * operations and queue dependent operations for processing */ -	case FSCACHE_OBJECT_AVAILABLE: -		fscache_object_available(object); -		goto active_transit; - -		/* normal running state */ -	case FSCACHE_OBJECT_ACTIVE: -		goto active_transit; - -		/* update the object metadata on disk */ -	case FSCACHE_OBJECT_UPDATING: -		clear_bit(FSCACHE_OBJECT_EV_UPDATE, &object->events); -		fscache_stat(&fscache_n_updates_run); -		fscache_stat(&fscache_n_cop_update_object); -		object->cache->ops->update_object(object); -		fscache_stat_d(&fscache_n_cop_update_object); -		goto active_transit; - -		/* handle an object dying during lookup or creation */ -	case FSCACHE_OBJECT_LC_DYING: -		object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE); -		fscache_stat(&fscache_n_cop_lookup_complete); -		object->cache->ops->lookup_complete(object); -		fscache_stat_d(&fscache_n_cop_lookup_complete); - -		spin_lock(&object->lock); -		object->state = FSCACHE_OBJECT_DYING; -		cookie = object->cookie; -		if (cookie) { -			if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, -					       &cookie->flags)) -				wake_up_bit(&cookie->flags, -					    FSCACHE_COOKIE_LOOKING_UP); -			if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, -					       &cookie->flags)) -				wake_up_bit(&cookie->flags, -					    FSCACHE_COOKIE_CREATING); +	       object->debug_id, object->state->name, object->events); + +	event_mask = object->event_mask; +restart: +	object->event_mask = 0; /* Mask normal event handling */ +	state = object->state; +restart_masked: +	events = object->events; + +	/* Handle any out-of-band events (typically an error) */ +	if (events & object->oob_event_mask) { +		_debug("{OBJ%x} oob %lx", +		       object->debug_id, events & object->oob_event_mask); +		for (t = object->oob_table; t->events; t++) { +			if (events & t->events) { +				state = t->transit_to; +				ASSERT(state->work != NULL); +				event = fls(events & t->events) - 1; +				__clear_bit(event, &object->oob_event_mask); +				clear_bit(event, &object->events); +				goto execute_work_state; +			}  		} -		spin_unlock(&object->lock); +	} -		fscache_done_parent_op(object); +	/* Wait states are just transition tables */ +	if (!state->work) { +		if (events & event_mask) { +			for (t = state->transitions; t->events; t++) { +				if (events & t->events) { +					new_state = t->transit_to; +					event = fls(events & t->events) - 1; +					clear_bit(event, &object->events); +					_debug("{OBJ%x} ev %d: %s -> %s", +					       object->debug_id, event, +					       state->name, new_state->name); +					object->state = state = new_state; +					goto execute_work_state; +				} +			} -		/* wait for completion of all active operations on this object -		 * and the death of all child objects of this object */ -	case FSCACHE_OBJECT_DYING: -	dying: -		clear_bit(FSCACHE_OBJECT_EV_CLEARED, &object->events); -		spin_lock(&object->lock); -		_debug("dying OBJ%x {%d,%d}", -		       object->debug_id, object->n_ops, object->n_children); -		if (object->n_ops == 0 && object->n_children == 0) { -			object->event_mask &= -				~(1 << FSCACHE_OBJECT_EV_CLEARED); -			object->event_mask |= -				(1 << FSCACHE_OBJECT_EV_WITHDRAW) | -				(1 << FSCACHE_OBJECT_EV_RETIRE) | -				(1 << FSCACHE_OBJECT_EV_RELEASE) | -				(1 << FSCACHE_OBJECT_EV_ERROR); -		} else { -			object->event_mask &= -				~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | -				  (1 << FSCACHE_OBJECT_EV_RETIRE) | -				  (1 << FSCACHE_OBJECT_EV_RELEASE) | -				  (1 << FSCACHE_OBJECT_EV_ERROR)); -			object->event_mask |= -				1 << FSCACHE_OBJECT_EV_CLEARED; +			/* The event mask didn't include all the tabled bits */ +			BUG();  		} -		spin_unlock(&object->lock); -		fscache_enqueue_dependents(object); -		fscache_start_operations(object); -		goto terminal_transit; - -		/* handle an abort during initialisation */ -	case FSCACHE_OBJECT_ABORT_INIT: -		_debug("handle abort init %lx", object->events); -		object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE); - -		spin_lock(&object->lock); -		fscache_dequeue_object(object); - -		object->state = FSCACHE_OBJECT_DYING; -		if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, -				       &object->cookie->flags)) -			wake_up_bit(&object->cookie->flags, -				    FSCACHE_COOKIE_CREATING); -		spin_unlock(&object->lock); -		goto dying; - -		/* handle the netfs releasing an object and possibly marking it -		 * obsolete too */ -	case FSCACHE_OBJECT_RELEASING: -	case FSCACHE_OBJECT_RECYCLING: -		object->event_mask &= -			~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | -			  (1 << FSCACHE_OBJECT_EV_RETIRE) | -			  (1 << FSCACHE_OBJECT_EV_RELEASE) | -			  (1 << FSCACHE_OBJECT_EV_ERROR)); -		fscache_release_object(object); -		spin_lock(&object->lock); -		object->state = FSCACHE_OBJECT_DEAD; -		spin_unlock(&object->lock); -		fscache_stat(&fscache_n_object_dead); -		goto terminal_transit; - -		/* handle the parent cache of this object being withdrawn from -		 * active service */ -	case FSCACHE_OBJECT_WITHDRAWING: -		object->event_mask &= -			~((1 << FSCACHE_OBJECT_EV_WITHDRAW) | -			  (1 << FSCACHE_OBJECT_EV_RETIRE) | -			  (1 << FSCACHE_OBJECT_EV_RELEASE) | -			  (1 << FSCACHE_OBJECT_EV_ERROR)); -		fscache_withdraw_object(object); -		spin_lock(&object->lock); -		object->state = FSCACHE_OBJECT_DEAD; -		spin_unlock(&object->lock); -		fscache_stat(&fscache_n_object_dead); -		goto terminal_transit; - -		/* complain about the object being woken up once it is -		 * deceased */ -	case FSCACHE_OBJECT_DEAD: -		printk(KERN_ERR "FS-Cache:" -		       " Unexpected event in dead state %lx\n", -		       object->events & object->event_mask); -		BUG(); - -	default: -		printk(KERN_ERR "FS-Cache: Unknown object state %u\n", -		       object->state); -		BUG(); +		/* Randomly woke up */ +		goto unmask_events;  	} -	/* determine the transition from a lookup state */ -lookup_transit: -	switch (fls(object->events & object->event_mask) - 1) { -	case FSCACHE_OBJECT_EV_WITHDRAW: -	case FSCACHE_OBJECT_EV_RETIRE: -	case FSCACHE_OBJECT_EV_RELEASE: -	case FSCACHE_OBJECT_EV_ERROR: -		new_state = FSCACHE_OBJECT_LC_DYING; -		goto change_state; -	case FSCACHE_OBJECT_EV_REQUEUE: -		goto done; -	case -1: -		goto done; /* sleep until event */ -	default: -		goto unsupported_event; -	} +execute_work_state: +	_debug("{OBJ%x} exec %s", object->debug_id, state->name); -	/* determine the transition from an active state */ -active_transit: -	switch (fls(object->events & object->event_mask) - 1) { -	case FSCACHE_OBJECT_EV_WITHDRAW: -	case FSCACHE_OBJECT_EV_RETIRE: -	case FSCACHE_OBJECT_EV_RELEASE: -	case FSCACHE_OBJECT_EV_ERROR: -		new_state = FSCACHE_OBJECT_DYING; -		goto change_state; -	case FSCACHE_OBJECT_EV_UPDATE: -		new_state = FSCACHE_OBJECT_UPDATING; -		goto change_state; -	case -1: -		new_state = FSCACHE_OBJECT_ACTIVE; -		goto change_state; /* sleep until event */ -	default: -		goto unsupported_event; +	new_state = state->work(object, event); +	event = -1; +	if (new_state == NO_TRANSIT) { +		_debug("{OBJ%x} %s notrans", object->debug_id, state->name); +		fscache_enqueue_object(object); +		event_mask = object->oob_event_mask; +		goto unmask_events;  	} -	/* determine the transition from a terminal state */ -terminal_transit: -	switch (fls(object->events & object->event_mask) - 1) { -	case FSCACHE_OBJECT_EV_WITHDRAW: -		new_state = FSCACHE_OBJECT_WITHDRAWING; -		goto change_state; -	case FSCACHE_OBJECT_EV_RETIRE: -		new_state = FSCACHE_OBJECT_RECYCLING; -		goto change_state; -	case FSCACHE_OBJECT_EV_RELEASE: -		new_state = FSCACHE_OBJECT_RELEASING; -		goto change_state; -	case FSCACHE_OBJECT_EV_ERROR: -		new_state = FSCACHE_OBJECT_WITHDRAWING; -		goto change_state; -	case FSCACHE_OBJECT_EV_CLEARED: -		new_state = FSCACHE_OBJECT_DYING; -		goto change_state; -	case -1: -		goto done; /* sleep until event */ -	default: -		goto unsupported_event; -	} +	_debug("{OBJ%x} %s -> %s", +	       object->debug_id, state->name, new_state->name); +	object->state = state = new_state; -change_state: -	spin_lock(&object->lock); -	object->state = new_state; -	spin_unlock(&object->lock); - -done: -	_leave(" [->%s]", fscache_object_states[object->state]); -	return; +	if (state->work) { +		if (unlikely(state->work == ((void *)2UL))) { +			_leave(" [dead]"); +			return; +		} +		goto restart_masked; +	} -unsupported_event: -	printk(KERN_ERR "FS-Cache:" -	       " Unsupported event %lx [mask %lx] in state %s\n", -	       object->events, object->event_mask, -	       fscache_object_states[object->state]); -	BUG(); +	/* Transited to wait state */ +	event_mask = object->oob_event_mask; +	for (t = state->transitions; t->events; t++) +		event_mask |= t->events; + +unmask_events: +	object->event_mask = event_mask; +	smp_mb(); +	events = object->events; +	if (events & event_mask) +		goto restart; +	_leave(" [msk %lx]", event_mask);  }  /*   * execute an object   */ -void fscache_object_work_func(struct work_struct *work) +static void fscache_object_work_func(struct work_struct *work)  {  	struct fscache_object *object =  		container_of(work, struct fscache_object, work); @@ -338,14 +272,73 @@ void fscache_object_work_func(struct work_struct *work)  	_enter("{OBJ%x}", object->debug_id);  	start = jiffies; -	fscache_object_state_machine(object); +	fscache_object_sm_dispatcher(object);  	fscache_hist(fscache_objs_histogram, start); -	if (object->events & object->event_mask) -		fscache_enqueue_object(object); -	clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events);  	fscache_put_object(object);  } -EXPORT_SYMBOL(fscache_object_work_func); + +/** + * fscache_object_init - Initialise a cache object description + * @object: Object description + * @cookie: Cookie object will be attached to + * @cache: Cache in which backing object will be found + * + * Initialise a cache object description to its basic values. + * + * See Documentation/filesystems/caching/backend-api.txt for a complete + * description. + */ +void fscache_object_init(struct fscache_object *object, +			 struct fscache_cookie *cookie, +			 struct fscache_cache *cache) +{ +	const struct fscache_transition *t; + +	atomic_inc(&cache->object_count); + +	object->state = STATE(WAIT_FOR_INIT); +	object->oob_table = fscache_osm_init_oob; +	object->flags = 1 << FSCACHE_OBJECT_IS_LIVE; +	spin_lock_init(&object->lock); +	INIT_LIST_HEAD(&object->cache_link); +	INIT_HLIST_NODE(&object->cookie_link); +	INIT_WORK(&object->work, fscache_object_work_func); +	INIT_LIST_HEAD(&object->dependents); +	INIT_LIST_HEAD(&object->dep_link); +	INIT_LIST_HEAD(&object->pending_ops); +	object->n_children = 0; +	object->n_ops = object->n_in_progress = object->n_exclusive = 0; +	object->events = 0; +	object->store_limit = 0; +	object->store_limit_l = 0; +	object->cache = cache; +	object->cookie = cookie; +	object->parent = NULL; +#ifdef CONFIG_FSCACHE_OBJECT_LIST +	RB_CLEAR_NODE(&object->objlist_link); +#endif + +	object->oob_event_mask = 0; +	for (t = object->oob_table; t->events; t++) +		object->oob_event_mask |= t->events; +	object->event_mask = object->oob_event_mask; +	for (t = object->state->transitions; t->events; t++) +		object->event_mask |= t->events; +} +EXPORT_SYMBOL(fscache_object_init); + +/* + * Abort object initialisation before we start it. + */ +static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *object, +								int event) +{ +	_enter("{OBJ%x},%d", object->debug_id, event); + +	object->oob_event_mask = 0; +	fscache_dequeue_object(object); +	return transit_to(KILL_OBJECT); +}  /*   * initialise an object @@ -353,130 +346,136 @@ EXPORT_SYMBOL(fscache_object_work_func);   *   immediately to do a creation   * - we may need to start the process of creating a parent and we need to wait   *   for the parent's lookup and creation to complete if it's not there yet - * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the - *   leaf-most cookies of the object and all its children   */ -static void fscache_initialise_object(struct fscache_object *object) +static const struct fscache_state *fscache_initialise_object(struct fscache_object *object, +							     int event)  {  	struct fscache_object *parent; +	bool success; -	_enter(""); -	ASSERT(object->cookie != NULL); -	ASSERT(object->cookie->parent != NULL); - -	if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | -			      (1 << FSCACHE_OBJECT_EV_RELEASE) | -			      (1 << FSCACHE_OBJECT_EV_RETIRE) | -			      (1 << FSCACHE_OBJECT_EV_WITHDRAW))) { -		_debug("abort init %lx", object->events); -		spin_lock(&object->lock); -		object->state = FSCACHE_OBJECT_ABORT_INIT; -		spin_unlock(&object->lock); -		return; -	} +	_enter("{OBJ%x},%d", object->debug_id, event); -	spin_lock(&object->cookie->lock); -	spin_lock_nested(&object->cookie->parent->lock, 1); +	ASSERT(list_empty(&object->dep_link));  	parent = object->parent;  	if (!parent) { -		_debug("no parent"); -		set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); -	} else { -		spin_lock(&object->lock); -		spin_lock_nested(&parent->lock, 1); -		_debug("parent %s", fscache_object_states[parent->state]); - -		if (parent->state >= FSCACHE_OBJECT_DYING) { -			_debug("bad parent"); -			set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); -		} else if (parent->state < FSCACHE_OBJECT_AVAILABLE) { -			_debug("wait"); - -			/* we may get woken up in this state by child objects -			 * binding on to us, so we need to make sure we don't -			 * add ourself to the list multiple times */ -			if (list_empty(&object->dep_link)) { -				fscache_stat(&fscache_n_cop_grab_object); -				object->cache->ops->grab_object(object); -				fscache_stat_d(&fscache_n_cop_grab_object); -				list_add(&object->dep_link, -					 &parent->dependents); - -				/* fscache_acquire_non_index_cookie() uses this -				 * to wake the chain up */ -				if (parent->state == FSCACHE_OBJECT_INIT) -					fscache_enqueue_object(parent); -			} -		} else { -			_debug("go"); -			parent->n_ops++; -			parent->n_obj_ops++; -			object->lookup_jif = jiffies; -			object->state = FSCACHE_OBJECT_LOOKING_UP; -			set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); -		} +		_leave(" [no parent]"); +		return transit_to(DROP_OBJECT); +	} -		spin_unlock(&parent->lock); -		spin_unlock(&object->lock); +	_debug("parent: %s of:%lx", parent->state->name, parent->flags); + +	if (fscache_object_is_dying(parent)) { +		_leave(" [bad parent]"); +		return transit_to(DROP_OBJECT); +	} + +	if (fscache_object_is_available(parent)) { +		_leave(" [ready]"); +		return transit_to(PARENT_READY); +	} + +	_debug("wait"); + +	spin_lock(&parent->lock); +	fscache_stat(&fscache_n_cop_grab_object); +	success = false; +	if (fscache_object_is_live(parent) && +	    object->cache->ops->grab_object(object)) { +		list_add(&object->dep_link, &parent->dependents); +		success = true; +	} +	fscache_stat_d(&fscache_n_cop_grab_object); +	spin_unlock(&parent->lock); +	if (!success) { +		_leave(" [grab failed]"); +		return transit_to(DROP_OBJECT);  	} -	spin_unlock(&object->cookie->parent->lock); -	spin_unlock(&object->cookie->lock); +	/* fscache_acquire_non_index_cookie() uses this +	 * to wake the chain up */ +	fscache_raise_event(parent, FSCACHE_OBJECT_EV_NEW_CHILD); +	_leave(" [wait]"); +	return transit_to(WAIT_FOR_PARENT); +} + +/* + * Once the parent object is ready, we should kick off our lookup op. + */ +static const struct fscache_state *fscache_parent_ready(struct fscache_object *object, +							int event) +{ +	struct fscache_object *parent = object->parent; + +	_enter("{OBJ%x},%d", object->debug_id, event); + +	ASSERT(parent != NULL); + +	spin_lock(&parent->lock); +	parent->n_ops++; +	parent->n_obj_ops++; +	object->lookup_jif = jiffies; +	spin_unlock(&parent->lock); +  	_leave(""); +	return transit_to(LOOK_UP_OBJECT);  }  /*   * look an object up in the cache from which it was allocated   * - we hold an "access lock" on the parent object, so the parent object cannot   *   be withdrawn by either party till we've finished - * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the - *   leaf-most cookies of the object and all its children   */ -static void fscache_lookup_object(struct fscache_object *object) +static const struct fscache_state *fscache_look_up_object(struct fscache_object *object, +							  int event)  {  	struct fscache_cookie *cookie = object->cookie; -	struct fscache_object *parent; +	struct fscache_object *parent = object->parent;  	int ret; -	_enter(""); +	_enter("{OBJ%x},%d", object->debug_id, event); + +	object->oob_table = fscache_osm_lookup_oob; -	parent = object->parent;  	ASSERT(parent != NULL);  	ASSERTCMP(parent->n_ops, >, 0);  	ASSERTCMP(parent->n_obj_ops, >, 0);  	/* make sure the parent is still available */ -	ASSERTCMP(parent->state, >=, FSCACHE_OBJECT_AVAILABLE); - -	if (parent->state >= FSCACHE_OBJECT_DYING || -	    test_bit(FSCACHE_IOERROR, &object->cache->flags)) { -		_debug("unavailable"); -		set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); -		_leave(""); -		return; +	ASSERT(fscache_object_is_available(parent)); + +	if (fscache_object_is_dying(parent) || +	    test_bit(FSCACHE_IOERROR, &object->cache->flags) || +	    !fscache_use_cookie(object)) { +		_leave(" [unavailable]"); +		return transit_to(LOOKUP_FAILURE);  	} -	_debug("LOOKUP \"%s/%s\" in \"%s\"", -	       parent->cookie->def->name, cookie->def->name, -	       object->cache->tag->name); +	_debug("LOOKUP \"%s\" in \"%s\"", +	       cookie->def->name, object->cache->tag->name);  	fscache_stat(&fscache_n_object_lookups);  	fscache_stat(&fscache_n_cop_lookup_object);  	ret = object->cache->ops->lookup_object(object);  	fscache_stat_d(&fscache_n_cop_lookup_object); -	if (test_bit(FSCACHE_OBJECT_EV_ERROR, &object->events)) -		set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); +	fscache_unuse_cookie(object);  	if (ret == -ETIMEDOUT) {  		/* probably stuck behind another object, so move this one to  		 * the back of the queue */  		fscache_stat(&fscache_n_object_lookups_timed_out); -		set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); +		_leave(" [timeout]"); +		return NO_TRANSIT;  	} -	_leave(""); +	if (ret < 0) { +		_leave(" [error]"); +		return transit_to(LOOKUP_FAILURE); +	} + +	_leave(" [ok]"); +	return transit_to(OBJECT_AVAILABLE);  }  /** @@ -490,32 +489,21 @@ void fscache_object_lookup_negative(struct fscache_object *object)  {  	struct fscache_cookie *cookie = object->cookie; -	_enter("{OBJ%x,%s}", -	       object->debug_id, fscache_object_states[object->state]); +	_enter("{OBJ%x,%s}", object->debug_id, object->state->name); -	spin_lock(&object->lock); -	if (object->state == FSCACHE_OBJECT_LOOKING_UP) { +	if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) {  		fscache_stat(&fscache_n_object_lookups_negative); -		/* transit here to allow write requests to begin stacking up -		 * and read requests to begin returning ENODATA */ -		object->state = FSCACHE_OBJECT_CREATING; -		spin_unlock(&object->lock); - -		set_bit(FSCACHE_COOKIE_PENDING_FILL, &cookie->flags); +		/* Allow write requests to begin stacking up and read requests to begin +		 * returning ENODATA. +		 */  		set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); +		clear_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags);  		_debug("wake up lookup %p", &cookie->flags); -		smp_mb__before_clear_bit(); -		clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); -		smp_mb__after_clear_bit(); +		clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags);  		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); -		set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); -	} else { -		ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING); -		spin_unlock(&object->lock);  	} -  	_leave("");  }  EXPORT_SYMBOL(fscache_object_lookup_negative); @@ -534,38 +522,27 @@ void fscache_obtained_object(struct fscache_object *object)  {  	struct fscache_cookie *cookie = object->cookie; -	_enter("{OBJ%x,%s}", -	       object->debug_id, fscache_object_states[object->state]); +	_enter("{OBJ%x,%s}", object->debug_id, object->state->name);  	/* if we were still looking up, then we must have a positive lookup  	 * result, in which case there may be data available */ -	spin_lock(&object->lock); -	if (object->state == FSCACHE_OBJECT_LOOKING_UP) { +	if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) {  		fscache_stat(&fscache_n_object_lookups_positive); -		clear_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); +		/* We do (presumably) have data */ +		clear_bit_unlock(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); +		clear_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); -		object->state = FSCACHE_OBJECT_AVAILABLE; -		spin_unlock(&object->lock); - -		smp_mb__before_clear_bit(); -		clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); -		smp_mb__after_clear_bit(); +		/* Allow write requests to begin stacking up and read requests +		 * to begin shovelling data. +		 */ +		clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags);  		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); -		set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events);  	} else { -		ASSERTCMP(object->state, ==, FSCACHE_OBJECT_CREATING);  		fscache_stat(&fscache_n_object_created); - -		object->state = FSCACHE_OBJECT_AVAILABLE; -		spin_unlock(&object->lock); -		set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); -		smp_wmb();  	} -	if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, &cookie->flags)) -		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_CREATING); - +	set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &object->flags);  	_leave("");  }  EXPORT_SYMBOL(fscache_obtained_object); @@ -573,22 +550,19 @@ EXPORT_SYMBOL(fscache_obtained_object);  /*   * handle an object that has just become available   */ -static void fscache_object_available(struct fscache_object *object) +static const struct fscache_state *fscache_object_available(struct fscache_object *object, +							    int event)  { -	_enter("{OBJ%x}", object->debug_id); +	_enter("{OBJ%x},%d", object->debug_id, event); -	spin_lock(&object->lock); +	object->oob_table = fscache_osm_run_oob; -	if (object->cookie && -	    test_and_clear_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags)) -		wake_up_bit(&object->cookie->flags, FSCACHE_COOKIE_CREATING); +	spin_lock(&object->lock);  	fscache_done_parent_op(object);  	if (object->n_in_progress == 0) {  		if (object->n_ops > 0) {  			ASSERTCMP(object->n_ops, >=, object->n_obj_ops); -			ASSERTIF(object->n_ops > object->n_obj_ops, -				 !list_empty(&object->pending_ops));  			fscache_start_operations(object);  		} else {  			ASSERT(list_empty(&object->pending_ops)); @@ -599,129 +573,159 @@ static void fscache_object_available(struct fscache_object *object)  	fscache_stat(&fscache_n_cop_lookup_complete);  	object->cache->ops->lookup_complete(object);  	fscache_stat_d(&fscache_n_cop_lookup_complete); -	fscache_enqueue_dependents(object);  	fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif);  	fscache_stat(&fscache_n_object_avail);  	_leave(""); +	return transit_to(JUMPSTART_DEPS);  }  /* - * drop an object's attachments + * Wake up this object's dependent objects now that we've become available.   */ -static void fscache_drop_object(struct fscache_object *object) +static const struct fscache_state *fscache_jumpstart_dependents(struct fscache_object *object, +								int event)  { -	struct fscache_object *parent = object->parent; -	struct fscache_cache *cache = object->cache; +	_enter("{OBJ%x},%d", object->debug_id, event); -	_enter("{OBJ%x,%d}", object->debug_id, object->n_children); +	if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_PARENT_READY)) +		return NO_TRANSIT; /* Not finished; requeue */ +	return transit_to(WAIT_FOR_CMD); +} -	ASSERTCMP(object->cookie, ==, NULL); -	ASSERT(hlist_unhashed(&object->cookie_link)); +/* + * Handle lookup or creation failute. + */ +static const struct fscache_state *fscache_lookup_failure(struct fscache_object *object, +							  int event) +{ +	struct fscache_cookie *cookie; -	spin_lock(&cache->object_list_lock); -	list_del_init(&object->cache_link); -	spin_unlock(&cache->object_list_lock); +	_enter("{OBJ%x},%d", object->debug_id, event); -	fscache_stat(&fscache_n_cop_drop_object); -	cache->ops->drop_object(object); -	fscache_stat_d(&fscache_n_cop_drop_object); +	object->oob_event_mask = 0; -	if (parent) { -		_debug("release parent OBJ%x {%d}", -		       parent->debug_id, parent->n_children); +	fscache_stat(&fscache_n_cop_lookup_complete); +	object->cache->ops->lookup_complete(object); +	fscache_stat_d(&fscache_n_cop_lookup_complete); -		spin_lock(&parent->lock); -		parent->n_children--; -		if (parent->n_children == 0) -			fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); -		spin_unlock(&parent->lock); -		object->parent = NULL; +	cookie = object->cookie; +	set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); +	if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) +		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); + +	fscache_done_parent_op(object); +	return transit_to(KILL_OBJECT); +} + +/* + * Wait for completion of all active operations on this object and the death of + * all child objects of this object. + */ +static const struct fscache_state *fscache_kill_object(struct fscache_object *object, +						       int event) +{ +	_enter("{OBJ%x,%d,%d},%d", +	       object->debug_id, object->n_ops, object->n_children, event); + +	clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); +	object->oob_event_mask = 0; + +	if (list_empty(&object->dependents) && +	    object->n_ops == 0 && +	    object->n_children == 0) +		return transit_to(DROP_OBJECT); + +	if (object->n_in_progress == 0) { +		spin_lock(&object->lock); +		if (object->n_ops > 0 && object->n_in_progress == 0) +			fscache_start_operations(object); +		spin_unlock(&object->lock);  	} -	/* this just shifts the object release to the work processor */ -	fscache_put_object(object); +	if (!list_empty(&object->dependents)) +		return transit_to(KILL_DEPENDENTS); -	_leave(""); +	return transit_to(WAIT_FOR_CLEARANCE);  }  /* - * release or recycle an object that the netfs has discarded + * Kill dependent objects.   */ -static void fscache_release_object(struct fscache_object *object) +static const struct fscache_state *fscache_kill_dependents(struct fscache_object *object, +							   int event)  { -	_enter(""); +	_enter("{OBJ%x},%d", object->debug_id, event); -	fscache_drop_object(object); +	if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_KILL)) +		return NO_TRANSIT; /* Not finished */ +	return transit_to(WAIT_FOR_CLEARANCE);  }  /* - * withdraw an object from active service + * Drop an object's attachments   */ -static void fscache_withdraw_object(struct fscache_object *object) +static const struct fscache_state *fscache_drop_object(struct fscache_object *object, +						       int event)  { -	struct fscache_cookie *cookie; -	bool detached; +	struct fscache_object *parent = object->parent; +	struct fscache_cookie *cookie = object->cookie; +	struct fscache_cache *cache = object->cache; +	bool awaken = false; -	_enter(""); +	_enter("{OBJ%x,%d},%d", object->debug_id, object->n_children, event); -	spin_lock(&object->lock); -	cookie = object->cookie; -	if (cookie) { -		/* need to get the cookie lock before the object lock, starting -		 * from the object pointer */ -		atomic_inc(&cookie->usage); -		spin_unlock(&object->lock); +	ASSERT(cookie != NULL); +	ASSERT(!hlist_unhashed(&object->cookie_link)); -		detached = false; -		spin_lock(&cookie->lock); -		spin_lock(&object->lock); +	/* Make sure the cookie no longer points here and that the netfs isn't +	 * waiting for us. +	 */ +	spin_lock(&cookie->lock); +	hlist_del_init(&object->cookie_link); +	if (hlist_empty(&cookie->backing_objects) && +	    test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) +		awaken = true; +	spin_unlock(&cookie->lock); -		if (object->cookie == cookie) { -			hlist_del_init(&object->cookie_link); -			object->cookie = NULL; -			detached = true; -		} -		spin_unlock(&cookie->lock); -		fscache_cookie_put(cookie); -		if (detached) -			fscache_cookie_put(cookie); -	} +	if (awaken) +		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); +	/* Prevent a race with our last child, which has to signal EV_CLEARED +	 * before dropping our spinlock. +	 */ +	spin_lock(&object->lock);  	spin_unlock(&object->lock); -	fscache_drop_object(object); -} +	/* Discard from the cache's collection of objects */ +	spin_lock(&cache->object_list_lock); +	list_del_init(&object->cache_link); +	spin_unlock(&cache->object_list_lock); -/* - * withdraw an object from active service at the behest of the cache - * - need break the links to a cached object cookie - * - called under two situations: - *   (1) recycler decides to reclaim an in-use object - *   (2) a cache is unmounted - * - have to take care as the cookie can be being relinquished by the netfs - *   simultaneously - * - the object is pinned by the caller holding a refcount on it - */ -void fscache_withdrawing_object(struct fscache_cache *cache, -				struct fscache_object *object) -{ -	bool enqueue = false; +	fscache_stat(&fscache_n_cop_drop_object); +	cache->ops->drop_object(object); +	fscache_stat_d(&fscache_n_cop_drop_object); -	_enter(",OBJ%x", object->debug_id); +	/* The parent object wants to know when all it dependents have gone */ +	if (parent) { +		_debug("release parent OBJ%x {%d}", +		       parent->debug_id, parent->n_children); -	spin_lock(&object->lock); -	if (object->state < FSCACHE_OBJECT_WITHDRAWING) { -		object->state = FSCACHE_OBJECT_WITHDRAWING; -		enqueue = true; +		spin_lock(&parent->lock); +		parent->n_children--; +		if (parent->n_children == 0) +			fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); +		spin_unlock(&parent->lock); +		object->parent = NULL;  	} -	spin_unlock(&object->lock); -	if (enqueue) -		fscache_enqueue_object(object); +	/* this just shifts the object release to the work processor */ +	fscache_put_object(object); +	fscache_stat(&fscache_n_object_dead);  	_leave(""); +	return transit_to(OBJECT_DEAD);  }  /* @@ -738,7 +742,7 @@ static int fscache_get_object(struct fscache_object *object)  }  /* - * discard a ref on a work item + * Discard a ref on an object   */  static void fscache_put_object(struct fscache_object *object)  { @@ -747,6 +751,22 @@ static void fscache_put_object(struct fscache_object *object)  	fscache_stat_d(&fscache_n_cop_put_object);  } +/** + * fscache_object_destroy - Note that a cache object is about to be destroyed + * @object: The object to be destroyed + * + * Note the imminent destruction and deallocation of a cache object record. + */ +void fscache_object_destroy(struct fscache_object *object) +{ +	fscache_objlist_remove(object); + +	/* We can get rid of the cookie now */ +	fscache_cookie_put(object->cookie); +	object->cookie = NULL; +} +EXPORT_SYMBOL(fscache_object_destroy); +  /*   * enqueue an object for metadata-type processing   */ @@ -770,7 +790,7 @@ void fscache_enqueue_object(struct fscache_object *object)  /**   * fscache_object_sleep_till_congested - Sleep until object wq is congested - * @timoutp: Scheduler sleep timeout + * @timeoutp: Scheduler sleep timeout   *   * Allow an object handler to sleep until the object workqueue is congested.   * @@ -782,7 +802,7 @@ void fscache_enqueue_object(struct fscache_object *object)   */  bool fscache_object_sleep_till_congested(signed long *timeoutp)  { -	wait_queue_head_t *cong_wq = &__get_cpu_var(fscache_object_cong_wait); +	wait_queue_head_t *cong_wq = this_cpu_ptr(&fscache_object_cong_wait);  	DEFINE_WAIT(wait);  	if (fscache_object_congested()) @@ -798,18 +818,21 @@ bool fscache_object_sleep_till_congested(signed long *timeoutp)  EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested);  /* - * enqueue the dependents of an object for metadata-type processing - * - the caller must hold the object's lock - * - this may cause an already locked object to wind up being processed again + * Enqueue the dependents of an object for metadata-type processing. + * + * If we don't manage to finish the list before the scheduler wants to run + * again then return false immediately.  We return true if the list was + * cleared.   */ -static void fscache_enqueue_dependents(struct fscache_object *object) +static bool fscache_enqueue_dependents(struct fscache_object *object, int event)  {  	struct fscache_object *dep; +	bool ret = true;  	_enter("{OBJ%x}", object->debug_id);  	if (list_empty(&object->dependents)) -		return; +		return true;  	spin_lock(&object->lock); @@ -818,23 +841,23 @@ static void fscache_enqueue_dependents(struct fscache_object *object)  				 struct fscache_object, dep_link);  		list_del_init(&dep->dep_link); - -		/* sort onto appropriate lists */ -		fscache_enqueue_object(dep); +		fscache_raise_event(dep, event);  		fscache_put_object(dep); -		if (!list_empty(&object->dependents)) -			cond_resched_lock(&object->lock); +		if (!list_empty(&object->dependents) && need_resched()) { +			ret = false; +			break; +		}  	}  	spin_unlock(&object->lock); +	return ret;  }  /*   * remove an object from whatever queue it's waiting on - * - the caller must hold object->lock   */ -void fscache_dequeue_object(struct fscache_object *object) +static void fscache_dequeue_object(struct fscache_object *object)  {  	_enter("{OBJ%x}", object->debug_id); @@ -853,7 +876,10 @@ void fscache_dequeue_object(struct fscache_object *object)   * @data: The auxiliary data for the object   * @datalen: The size of the auxiliary data   * - * This function consults the netfs about the coherency state of an object + * This function consults the netfs about the coherency state of an object. + * The caller must be holding a ref on cookie->n_active (held by + * fscache_look_up_object() on behalf of the cache backend during object lookup + * and creation).   */  enum fscache_checkaux fscache_check_aux(struct fscache_object *object,  					const void *data, uint16_t datalen) @@ -890,3 +916,102 @@ enum fscache_checkaux fscache_check_aux(struct fscache_object *object,  	return result;  }  EXPORT_SYMBOL(fscache_check_aux); + +/* + * Asynchronously invalidate an object. + */ +static const struct fscache_state *_fscache_invalidate_object(struct fscache_object *object, +							      int event) +{ +	struct fscache_operation *op; +	struct fscache_cookie *cookie = object->cookie; + +	_enter("{OBJ%x},%d", object->debug_id, event); + +	/* We're going to need the cookie.  If the cookie is not available then +	 * retire the object instead. +	 */ +	if (!fscache_use_cookie(object)) { +		ASSERT(object->cookie->stores.rnode == NULL); +		set_bit(FSCACHE_OBJECT_RETIRED, &object->flags); +		_leave(" [no cookie]"); +		return transit_to(KILL_OBJECT); +	} + +	/* Reject any new read/write ops and abort any that are pending. */ +	fscache_invalidate_writes(cookie); +	clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); +	fscache_cancel_all_ops(object); + +	/* Now we have to wait for in-progress reads and writes */ +	op = kzalloc(sizeof(*op), GFP_KERNEL); +	if (!op) +		goto nomem; + +	fscache_operation_init(op, object->cache->ops->invalidate_object, NULL); +	op->flags = FSCACHE_OP_ASYNC | +		(1 << FSCACHE_OP_EXCLUSIVE) | +		(1 << FSCACHE_OP_UNUSE_COOKIE); + +	spin_lock(&cookie->lock); +	if (fscache_submit_exclusive_op(object, op) < 0) +		goto submit_op_failed; +	spin_unlock(&cookie->lock); +	fscache_put_operation(op); + +	/* Once we've completed the invalidation, we know there will be no data +	 * stored in the cache and thus we can reinstate the data-check-skip +	 * optimisation. +	 */ +	set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); + +	/* We can allow read and write requests to come in once again.  They'll +	 * queue up behind our exclusive invalidation operation. +	 */ +	if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) +		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); +	_leave(" [ok]"); +	return transit_to(UPDATE_OBJECT); + +nomem: +	clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); +	fscache_unuse_cookie(object); +	_leave(" [ENOMEM]"); +	return transit_to(KILL_OBJECT); + +submit_op_failed: +	clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); +	spin_unlock(&cookie->lock); +	kfree(op); +	_leave(" [EIO]"); +	return transit_to(KILL_OBJECT); +} + +static const struct fscache_state *fscache_invalidate_object(struct fscache_object *object, +							     int event) +{ +	const struct fscache_state *s; + +	fscache_stat(&fscache_n_invalidates_run); +	fscache_stat(&fscache_n_cop_invalidate_object); +	s = _fscache_invalidate_object(object, event); +	fscache_stat_d(&fscache_n_cop_invalidate_object); +	return s; +} + +/* + * Asynchronously update an object. + */ +static const struct fscache_state *fscache_update_object(struct fscache_object *object, +							 int event) +{ +	_enter("{OBJ%x},%d", object->debug_id, event); + +	fscache_stat(&fscache_n_updates_run); +	fscache_stat(&fscache_n_cop_update_object); +	object->cache->ops->update_object(object); +	fscache_stat_d(&fscache_n_cop_update_object); + +	_leave(""); +	return transit_to(WAIT_FOR_CMD); +} diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c index b9f34eaede0..e7b87a0e518 100644 --- a/fs/fscache/operation.c +++ b/fs/fscache/operation.c @@ -33,12 +33,11 @@ void fscache_enqueue_operation(struct fscache_operation *op)  	_enter("{OBJ%x OP%x,%u}",  	       op->object->debug_id, op->debug_id, atomic_read(&op->usage)); -	fscache_set_op_state(op, "EnQ"); -  	ASSERT(list_empty(&op->pend_link));  	ASSERT(op->processor != NULL); -	ASSERTCMP(op->object->state, >=, FSCACHE_OBJECT_AVAILABLE); +	ASSERT(fscache_object_is_available(op->object));  	ASSERTCMP(atomic_read(&op->usage), >, 0); +	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_IN_PROGRESS);  	fscache_stat(&fscache_n_op_enqueue);  	switch (op->flags & FSCACHE_OP_TYPE) { @@ -52,8 +51,7 @@ void fscache_enqueue_operation(struct fscache_operation *op)  		_debug("queue for caller's attention");  		break;  	default: -		printk(KERN_ERR "FS-Cache: Unexpected op type %lx", -		       op->flags); +		pr_err("Unexpected op type %lx", op->flags);  		BUG();  		break;  	} @@ -66,8 +64,9 @@ EXPORT_SYMBOL(fscache_enqueue_operation);  static void fscache_run_op(struct fscache_object *object,  			   struct fscache_operation *op)  { -	fscache_set_op_state(op, "Run"); +	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING); +	op->state = FSCACHE_OP_ST_IN_PROGRESS;  	object->n_in_progress++;  	if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))  		wake_up_bit(&op->flags, FSCACHE_OP_WAITING); @@ -88,20 +87,21 @@ int fscache_submit_exclusive_op(struct fscache_object *object,  	_enter("{OBJ%x OP%x},", object->debug_id, op->debug_id); -	fscache_set_op_state(op, "SubmitX"); +	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED); +	ASSERTCMP(atomic_read(&op->usage), >, 0);  	spin_lock(&object->lock);  	ASSERTCMP(object->n_ops, >=, object->n_in_progress);  	ASSERTCMP(object->n_ops, >=, object->n_exclusive);  	ASSERT(list_empty(&op->pend_link)); -	ret = -ENOBUFS; +	op->state = FSCACHE_OP_ST_PENDING;  	if (fscache_object_is_active(object)) {  		op->object = object;  		object->n_ops++;  		object->n_exclusive++;	/* reads and writes must wait */ -		if (object->n_ops > 0) { +		if (object->n_in_progress > 0) {  			atomic_inc(&op->usage);  			list_add_tail(&op->pend_link, &object->pending_ops);  			fscache_stat(&fscache_n_op_pend); @@ -118,7 +118,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object,  		/* need to issue a new write op after this */  		clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);  		ret = 0; -	} else if (object->state == FSCACHE_OBJECT_CREATING) { +	} else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) {  		op->object = object;  		object->n_ops++;  		object->n_exclusive++;	/* reads and writes must wait */ @@ -127,8 +127,11 @@ int fscache_submit_exclusive_op(struct fscache_object *object,  		fscache_stat(&fscache_n_op_pend);  		ret = 0;  	} else { -		/* not allowed to submit ops in any other state */ -		BUG(); +		/* If we're in any other state, there must have been an I/O +		 * error of some nature. +		 */ +		ASSERT(test_bit(FSCACHE_IOERROR, &object->cache->flags)); +		ret = -EIO;  	}  	spin_unlock(&object->lock); @@ -140,7 +143,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object,   */  static void fscache_report_unexpected_submission(struct fscache_object *object,  						 struct fscache_operation *op, -						 unsigned long ostate) +						 const struct fscache_state *ostate)  {  	static bool once_only;  	struct fscache_operation *p; @@ -151,11 +154,8 @@ static void fscache_report_unexpected_submission(struct fscache_object *object,  	once_only = true;  	kdebug("unexpected submission OP%x [OBJ%x %s]", -	       op->debug_id, object->debug_id, -	       fscache_object_states[object->state]); -	kdebug("objstate=%s [%s]", -	       fscache_object_states[object->state], -	       fscache_object_states[ostate]); +	       op->debug_id, object->debug_id, object->state->name); +	kdebug("objstate=%s [%s]", object->state->name, ostate->name);  	kdebug("objflags=%lx", object->flags);  	kdebug("objevent=%lx [%lx]", object->events, object->event_mask);  	kdebug("ops=%u inp=%u exc=%u", @@ -186,16 +186,15 @@ static void fscache_report_unexpected_submission(struct fscache_object *object,  int fscache_submit_op(struct fscache_object *object,  		      struct fscache_operation *op)  { -	unsigned long ostate; +	const struct fscache_state *ostate;  	int ret;  	_enter("{OBJ%x OP%x},{%u}",  	       object->debug_id, op->debug_id, atomic_read(&op->usage)); +	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED);  	ASSERTCMP(atomic_read(&op->usage), >, 0); -	fscache_set_op_state(op, "Submit"); -  	spin_lock(&object->lock);  	ASSERTCMP(object->n_ops, >=, object->n_in_progress);  	ASSERTCMP(object->n_ops, >=, object->n_exclusive); @@ -204,6 +203,7 @@ int fscache_submit_op(struct fscache_object *object,  	ostate = object->state;  	smp_rmb(); +	op->state = FSCACHE_OP_ST_PENDING;  	if (fscache_object_is_active(object)) {  		op->object = object;  		object->n_ops++; @@ -222,23 +222,24 @@ int fscache_submit_op(struct fscache_object *object,  			fscache_run_op(object, op);  		}  		ret = 0; -	} else if (object->state == FSCACHE_OBJECT_CREATING) { +	} else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) {  		op->object = object;  		object->n_ops++;  		atomic_inc(&op->usage);  		list_add_tail(&op->pend_link, &object->pending_ops);  		fscache_stat(&fscache_n_op_pend);  		ret = 0; -	} else if (object->state == FSCACHE_OBJECT_DYING || -		   object->state == FSCACHE_OBJECT_LC_DYING || -		   object->state == FSCACHE_OBJECT_WITHDRAWING) { +	} else if (fscache_object_is_dying(object)) {  		fscache_stat(&fscache_n_op_rejected); +		op->state = FSCACHE_OP_ST_CANCELLED;  		ret = -ENOBUFS;  	} else if (!test_bit(FSCACHE_IOERROR, &object->cache->flags)) {  		fscache_report_unexpected_submission(object, op, ostate);  		ASSERT(!fscache_object_is_active(object)); +		op->state = FSCACHE_OP_ST_CANCELLED;  		ret = -ENOBUFS;  	} else { +		op->state = FSCACHE_OP_ST_CANCELLED;  		ret = -ENOBUFS;  	} @@ -258,8 +259,8 @@ void fscache_abort_object(struct fscache_object *object)  }  /* - * jump start the operation processing on an object - * - caller must hold object->lock + * Jump start the operation processing on an object.  The caller must hold + * object->lock.   */  void fscache_start_operations(struct fscache_object *object)  { @@ -291,20 +292,28 @@ void fscache_start_operations(struct fscache_object *object)  /*   * cancel an operation that's pending on an object   */ -int fscache_cancel_op(struct fscache_operation *op) +int fscache_cancel_op(struct fscache_operation *op, +		      void (*do_cancel)(struct fscache_operation *))  {  	struct fscache_object *object = op->object;  	int ret;  	_enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id); +	ASSERTCMP(op->state, >=, FSCACHE_OP_ST_PENDING); +	ASSERTCMP(op->state, !=, FSCACHE_OP_ST_CANCELLED); +	ASSERTCMP(atomic_read(&op->usage), >, 0); +  	spin_lock(&object->lock);  	ret = -EBUSY; -	if (!list_empty(&op->pend_link)) { +	if (op->state == FSCACHE_OP_ST_PENDING) { +		ASSERT(!list_empty(&op->pend_link));  		fscache_stat(&fscache_n_op_cancelled);  		list_del_init(&op->pend_link); -		object->n_ops--; +		if (do_cancel) +			do_cancel(op); +		op->state = FSCACHE_OP_ST_CANCELLED;  		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))  			object->n_exclusive--;  		if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) @@ -319,6 +328,70 @@ int fscache_cancel_op(struct fscache_operation *op)  }  /* + * Cancel all pending operations on an object + */ +void fscache_cancel_all_ops(struct fscache_object *object) +{ +	struct fscache_operation *op; + +	_enter("OBJ%x", object->debug_id); + +	spin_lock(&object->lock); + +	while (!list_empty(&object->pending_ops)) { +		op = list_entry(object->pending_ops.next, +				struct fscache_operation, pend_link); +		fscache_stat(&fscache_n_op_cancelled); +		list_del_init(&op->pend_link); + +		ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING); +		op->state = FSCACHE_OP_ST_CANCELLED; + +		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) +			object->n_exclusive--; +		if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) +			wake_up_bit(&op->flags, FSCACHE_OP_WAITING); +		fscache_put_operation(op); +		cond_resched_lock(&object->lock); +	} + +	spin_unlock(&object->lock); +	_leave(""); +} + +/* + * Record the completion or cancellation of an in-progress operation. + */ +void fscache_op_complete(struct fscache_operation *op, bool cancelled) +{ +	struct fscache_object *object = op->object; + +	_enter("OBJ%x", object->debug_id); + +	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_IN_PROGRESS); +	ASSERTCMP(object->n_in_progress, >, 0); +	ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags), +		    object->n_exclusive, >, 0); +	ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags), +		    object->n_in_progress, ==, 1); + +	spin_lock(&object->lock); + +	op->state = cancelled ? +		FSCACHE_OP_ST_CANCELLED : FSCACHE_OP_ST_COMPLETE; + +	if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) +		object->n_exclusive--; +	object->n_in_progress--; +	if (object->n_in_progress == 0) +		fscache_start_operations(object); + +	spin_unlock(&object->lock); +	_leave(""); +} +EXPORT_SYMBOL(fscache_op_complete); + +/*   * release an operation   * - queues pending ops if this is the last in-progress op   */ @@ -335,11 +408,10 @@ void fscache_put_operation(struct fscache_operation *op)  	if (!atomic_dec_and_test(&op->usage))  		return; -	fscache_set_op_state(op, "Put"); -  	_debug("PUT OP"); -	if (test_and_set_bit(FSCACHE_OP_DEAD, &op->flags)) -		BUG(); +	ASSERTIFCMP(op->state != FSCACHE_OP_ST_COMPLETE, +		    op->state, ==, FSCACHE_OP_ST_CANCELLED); +	op->state = FSCACHE_OP_ST_DEAD;  	fscache_stat(&fscache_n_op_release); @@ -352,6 +424,8 @@ void fscache_put_operation(struct fscache_operation *op)  	if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags))  		atomic_dec(&object->n_reads); +	if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags)) +		fscache_unuse_cookie(object);  	/* now... we may get called with the object spinlock held, so we  	 * complete the cleanup here only if we can immediately acquire the @@ -369,16 +443,6 @@ void fscache_put_operation(struct fscache_operation *op)  		return;  	} -	if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) { -		ASSERTCMP(object->n_exclusive, >, 0); -		object->n_exclusive--; -	} - -	ASSERTCMP(object->n_in_progress, >, 0); -	object->n_in_progress--; -	if (object->n_in_progress == 0) -		fscache_start_operations(object); -  	ASSERTCMP(object->n_ops, >, 0);  	object->n_ops--;  	if (object->n_ops == 0) @@ -417,23 +481,14 @@ void fscache_operation_gc(struct work_struct *work)  		spin_unlock(&cache->op_gc_list_lock);  		object = op->object; +		spin_lock(&object->lock);  		_debug("GC DEFERRED REL OBJ%x OP%x",  		       object->debug_id, op->debug_id);  		fscache_stat(&fscache_n_op_gc);  		ASSERTCMP(atomic_read(&op->usage), ==, 0); - -		spin_lock(&object->lock); -		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) { -			ASSERTCMP(object->n_exclusive, >, 0); -			object->n_exclusive--; -		} - -		ASSERTCMP(object->n_in_progress, >, 0); -		object->n_in_progress--; -		if (object->n_in_progress == 0) -			fscache_start_operations(object); +		ASSERTCMP(op->state, ==, FSCACHE_OP_ST_DEAD);  		ASSERTCMP(object->n_ops, >, 0);  		object->n_ops--; @@ -441,6 +496,7 @@ void fscache_operation_gc(struct work_struct *work)  			fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED);  		spin_unlock(&object->lock); +		kfree(op);  	} while (count++ < 20); diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 41c441c2058..ed70714503f 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -56,6 +56,7 @@ bool __fscache_maybe_release_page(struct fscache_cookie *cookie,  	_enter("%p,%p,%x", cookie, page, gfp); +try_again:  	rcu_read_lock();  	val = radix_tree_lookup(&cookie->stores, page->index);  	if (!val) { @@ -104,11 +105,19 @@ bool __fscache_maybe_release_page(struct fscache_cookie *cookie,  	return true;  page_busy: -	/* we might want to wait here, but that could deadlock the allocator as -	 * the work threads writing to the cache may all end up sleeping -	 * on memory allocation */ -	fscache_stat(&fscache_n_store_vmscan_busy); -	return false; +	/* We will wait here if we're allowed to, but that could deadlock the +	 * allocator as the work threads writing to the cache may all end up +	 * sleeping on memory allocation, so we may need to impose a timeout +	 * too. */ +	if (!(gfp & __GFP_WAIT) || !(gfp & __GFP_FS)) { +		fscache_stat(&fscache_n_store_vmscan_busy); +		return false; +	} + +	fscache_stat(&fscache_n_store_vmscan_wait); +	__fscache_wait_on_page_write(cookie, page); +	gfp &= ~__GFP_WAIT; +	goto try_again;  }  EXPORT_SYMBOL(__fscache_maybe_release_page); @@ -155,15 +164,14 @@ static void fscache_attr_changed_op(struct fscache_operation *op)  	fscache_stat(&fscache_n_attr_changed_calls);  	if (fscache_object_is_active(object)) { -		fscache_set_op_state(op, "CallFS");  		fscache_stat(&fscache_n_cop_attr_changed);  		ret = object->cache->ops->attr_changed(object);  		fscache_stat_d(&fscache_n_cop_attr_changed); -		fscache_set_op_state(op, "Done");  		if (ret < 0)  			fscache_abort_object(object);  	} +	fscache_op_complete(op, true);  	_leave("");  } @@ -174,6 +182,7 @@ int __fscache_attr_changed(struct fscache_cookie *cookie)  {  	struct fscache_operation *op;  	struct fscache_object *object; +	bool wake_cookie;  	_enter("%p", cookie); @@ -189,16 +198,19 @@ int __fscache_attr_changed(struct fscache_cookie *cookie)  	}  	fscache_operation_init(op, fscache_attr_changed_op, NULL); -	op->flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_EXCLUSIVE); -	fscache_set_op_name(op, "Attr"); +	op->flags = FSCACHE_OP_ASYNC | +		(1 << FSCACHE_OP_EXCLUSIVE) | +		(1 << FSCACHE_OP_UNUSE_COOKIE);  	spin_lock(&cookie->lock); -	if (hlist_empty(&cookie->backing_objects)) +	if (!fscache_cookie_enabled(cookie) || +	    hlist_empty(&cookie->backing_objects))  		goto nobufs;  	object = hlist_entry(cookie->backing_objects.first,  			     struct fscache_object, cookie_link); +	__fscache_use_cookie(cookie);  	if (fscache_submit_exclusive_op(object, op) < 0)  		goto nobufs;  	spin_unlock(&cookie->lock); @@ -208,8 +220,11 @@ int __fscache_attr_changed(struct fscache_cookie *cookie)  	return 0;  nobufs: +	wake_cookie = __fscache_unuse_cookie(cookie);  	spin_unlock(&cookie->lock);  	kfree(op); +	if (wake_cookie) +		__fscache_wake_unused_cookie(cookie);  	fscache_stat(&fscache_n_attr_changed_nobufs);  	_leave(" = %d", -ENOBUFS);  	return -ENOBUFS; @@ -226,6 +241,8 @@ static void fscache_release_retrieval_op(struct fscache_operation *_op)  	_enter("{OP%x}", op->op.debug_id); +	ASSERTCMP(atomic_read(&op->n_pages), ==, 0); +  	fscache_hist(fscache_retrieval_histogram, op->start_time);  	if (op->context)  		fscache_put_context(op->op.object->cookie, op->context); @@ -237,6 +254,7 @@ static void fscache_release_retrieval_op(struct fscache_operation *_op)   * allocate a retrieval op   */  static struct fscache_retrieval *fscache_alloc_retrieval( +	struct fscache_cookie *cookie,  	struct address_space *mapping,  	fscache_rw_complete_t end_io_func,  	void *context) @@ -251,20 +269,21 @@ static struct fscache_retrieval *fscache_alloc_retrieval(  	}  	fscache_operation_init(&op->op, NULL, fscache_release_retrieval_op); -	op->op.flags	= FSCACHE_OP_MYTHREAD | (1 << FSCACHE_OP_WAITING); +	op->op.flags	= FSCACHE_OP_MYTHREAD | +		(1UL << FSCACHE_OP_WAITING) | +		(1UL << FSCACHE_OP_UNUSE_COOKIE);  	op->mapping	= mapping;  	op->end_io_func	= end_io_func;  	op->context	= context;  	op->start_time	= jiffies;  	INIT_LIST_HEAD(&op->to_do); -	fscache_set_op_name(&op->op, "Retr");  	return op;  }  /*   * wait for a deferred lookup to complete   */ -static int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie) +int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie)  {  	unsigned long jif; @@ -295,37 +314,59 @@ static int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie)  }  /* + * Handle cancellation of a pending retrieval op + */ +static void fscache_do_cancel_retrieval(struct fscache_operation *_op) +{ +	struct fscache_retrieval *op = +		container_of(_op, struct fscache_retrieval, op); + +	atomic_set(&op->n_pages, 0); +} + +/*   * wait for an object to become active (or dead)   */ -static int fscache_wait_for_retrieval_activation(struct fscache_object *object, -						 struct fscache_retrieval *op, -						 atomic_t *stat_op_waits, -						 atomic_t *stat_object_dead) +int fscache_wait_for_operation_activation(struct fscache_object *object, +					  struct fscache_operation *op, +					  atomic_t *stat_op_waits, +					  atomic_t *stat_object_dead, +					  void (*do_cancel)(struct fscache_operation *))  {  	int ret; -	if (!test_bit(FSCACHE_OP_WAITING, &op->op.flags)) +	if (!test_bit(FSCACHE_OP_WAITING, &op->flags))  		goto check_if_dead;  	_debug(">>> WT"); -	fscache_stat(stat_op_waits); -	if (wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING, +	if (stat_op_waits) +		fscache_stat(stat_op_waits); +	if (wait_on_bit(&op->flags, FSCACHE_OP_WAITING,  			fscache_wait_bit_interruptible, -			TASK_INTERRUPTIBLE) < 0) { -		ret = fscache_cancel_op(&op->op); +			TASK_INTERRUPTIBLE) != 0) { +		ret = fscache_cancel_op(op, do_cancel);  		if (ret == 0)  			return -ERESTARTSYS;  		/* it's been removed from the pending queue by another party,  		 * so we should get to run shortly */ -		wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING, +		wait_on_bit(&op->flags, FSCACHE_OP_WAITING,  			    fscache_wait_bit, TASK_UNINTERRUPTIBLE);  	}  	_debug("<<< GO");  check_if_dead: +	if (op->state == FSCACHE_OP_ST_CANCELLED) { +		if (stat_object_dead) +			fscache_stat(stat_object_dead); +		_leave(" = -ENOBUFS [cancelled]"); +		return -ENOBUFS; +	}  	if (unlikely(fscache_object_is_dead(object))) { -		fscache_stat(stat_object_dead); +		pr_err("%s() = -ENOBUFS [obj dead %d]\n", __func__, op->state); +		fscache_cancel_op(op, do_cancel); +		if (stat_object_dead) +			fscache_stat(stat_object_dead);  		return -ENOBUFS;  	}  	return 0; @@ -348,6 +389,7 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,  {  	struct fscache_retrieval *op;  	struct fscache_object *object; +	bool wake_cookie = false;  	int ret;  	_enter("%p,%p,,,", cookie, page); @@ -357,33 +399,41 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,  	if (hlist_empty(&cookie->backing_objects))  		goto nobufs; +	if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { +		_leave(" = -ENOBUFS [invalidating]"); +		return -ENOBUFS; +	} +  	ASSERTCMP(cookie->def->type, !=, FSCACHE_COOKIE_TYPE_INDEX);  	ASSERTCMP(page, !=, NULL);  	if (fscache_wait_for_deferred_lookup(cookie) < 0)  		return -ERESTARTSYS; -	op = fscache_alloc_retrieval(page->mapping, end_io_func, context); +	op = fscache_alloc_retrieval(cookie, page->mapping, +				     end_io_func, context);  	if (!op) {  		_leave(" = -ENOMEM");  		return -ENOMEM;  	} -	fscache_set_op_name(&op->op, "RetrRA1"); +	atomic_set(&op->n_pages, 1);  	spin_lock(&cookie->lock); -	if (hlist_empty(&cookie->backing_objects)) +	if (!fscache_cookie_enabled(cookie) || +	    hlist_empty(&cookie->backing_objects))  		goto nobufs_unlock;  	object = hlist_entry(cookie->backing_objects.first,  			     struct fscache_object, cookie_link); -	ASSERTCMP(object->state, >, FSCACHE_OBJECT_LOOKING_UP); +	ASSERT(test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)); +	__fscache_use_cookie(cookie);  	atomic_inc(&object->n_reads); -	set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); +	__set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags);  	if (fscache_submit_op(object, &op->op) < 0) -		goto nobufs_unlock; +		goto nobufs_unlock_dec;  	spin_unlock(&cookie->lock);  	fscache_stat(&fscache_n_retrieval_ops); @@ -394,10 +444,11 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,  	/* we wait for the operation to become active, and then process it  	 * *here*, in this thread, and not in the thread pool */ -	ret = fscache_wait_for_retrieval_activation( -		object, op, +	ret = fscache_wait_for_operation_activation( +		object, &op->op,  		__fscache_stat(&fscache_n_retrieval_op_waits), -		__fscache_stat(&fscache_n_retrievals_object_dead)); +		__fscache_stat(&fscache_n_retrievals_object_dead), +		fscache_do_cancel_retrieval);  	if (ret < 0)  		goto error; @@ -430,8 +481,13 @@ error:  	_leave(" = %d", ret);  	return ret; +nobufs_unlock_dec: +	atomic_dec(&object->n_reads); +	wake_cookie = __fscache_unuse_cookie(cookie);  nobufs_unlock:  	spin_unlock(&cookie->lock); +	if (wake_cookie) +		__fscache_wake_unused_cookie(cookie);  	kfree(op);  nobufs:  	fscache_stat(&fscache_n_retrievals_nobufs); @@ -468,6 +524,7 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  {  	struct fscache_retrieval *op;  	struct fscache_object *object; +	bool wake_cookie = false;  	int ret;  	_enter("%p,,%d,,,", cookie, *nr_pages); @@ -477,6 +534,11 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  	if (hlist_empty(&cookie->backing_objects))  		goto nobufs; +	if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { +		_leave(" = -ENOBUFS [invalidating]"); +		return -ENOBUFS; +	} +  	ASSERTCMP(cookie->def->type, !=, FSCACHE_COOKIE_TYPE_INDEX);  	ASSERTCMP(*nr_pages, >, 0);  	ASSERT(!list_empty(pages)); @@ -484,23 +546,25 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  	if (fscache_wait_for_deferred_lookup(cookie) < 0)  		return -ERESTARTSYS; -	op = fscache_alloc_retrieval(mapping, end_io_func, context); +	op = fscache_alloc_retrieval(cookie, mapping, end_io_func, context);  	if (!op)  		return -ENOMEM; -	fscache_set_op_name(&op->op, "RetrRAN"); +	atomic_set(&op->n_pages, *nr_pages);  	spin_lock(&cookie->lock); -	if (hlist_empty(&cookie->backing_objects)) +	if (!fscache_cookie_enabled(cookie) || +	    hlist_empty(&cookie->backing_objects))  		goto nobufs_unlock;  	object = hlist_entry(cookie->backing_objects.first,  			     struct fscache_object, cookie_link); +	__fscache_use_cookie(cookie);  	atomic_inc(&object->n_reads); -	set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); +	__set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags);  	if (fscache_submit_op(object, &op->op) < 0) -		goto nobufs_unlock; +		goto nobufs_unlock_dec;  	spin_unlock(&cookie->lock);  	fscache_stat(&fscache_n_retrieval_ops); @@ -511,10 +575,11 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  	/* we wait for the operation to become active, and then process it  	 * *here*, in this thread, and not in the thread pool */ -	ret = fscache_wait_for_retrieval_activation( -		object, op, +	ret = fscache_wait_for_operation_activation( +		object, &op->op,  		__fscache_stat(&fscache_n_retrieval_op_waits), -		__fscache_stat(&fscache_n_retrievals_object_dead)); +		__fscache_stat(&fscache_n_retrievals_object_dead), +		fscache_do_cancel_retrieval);  	if (ret < 0)  		goto error; @@ -547,9 +612,14 @@ error:  	_leave(" = %d", ret);  	return ret; +nobufs_unlock_dec: +	atomic_dec(&object->n_reads); +	wake_cookie = __fscache_unuse_cookie(cookie);  nobufs_unlock:  	spin_unlock(&cookie->lock);  	kfree(op); +	if (wake_cookie) +		__fscache_wake_unused_cookie(cookie);  nobufs:  	fscache_stat(&fscache_n_retrievals_nobufs);  	_leave(" = -ENOBUFS"); @@ -571,6 +641,7 @@ int __fscache_alloc_page(struct fscache_cookie *cookie,  {  	struct fscache_retrieval *op;  	struct fscache_object *object; +	bool wake_cookie = false;  	int ret;  	_enter("%p,%p,,,", cookie, page); @@ -583,31 +654,39 @@ int __fscache_alloc_page(struct fscache_cookie *cookie,  	ASSERTCMP(cookie->def->type, !=, FSCACHE_COOKIE_TYPE_INDEX);  	ASSERTCMP(page, !=, NULL); +	if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { +		_leave(" = -ENOBUFS [invalidating]"); +		return -ENOBUFS; +	} +  	if (fscache_wait_for_deferred_lookup(cookie) < 0)  		return -ERESTARTSYS; -	op = fscache_alloc_retrieval(page->mapping, NULL, NULL); +	op = fscache_alloc_retrieval(cookie, page->mapping, NULL, NULL);  	if (!op)  		return -ENOMEM; -	fscache_set_op_name(&op->op, "RetrAL1"); +	atomic_set(&op->n_pages, 1);  	spin_lock(&cookie->lock); -	if (hlist_empty(&cookie->backing_objects)) +	if (!fscache_cookie_enabled(cookie) || +	    hlist_empty(&cookie->backing_objects))  		goto nobufs_unlock;  	object = hlist_entry(cookie->backing_objects.first,  			     struct fscache_object, cookie_link); +	__fscache_use_cookie(cookie);  	if (fscache_submit_op(object, &op->op) < 0) -		goto nobufs_unlock; +		goto nobufs_unlock_dec;  	spin_unlock(&cookie->lock);  	fscache_stat(&fscache_n_alloc_ops); -	ret = fscache_wait_for_retrieval_activation( -		object, op, +	ret = fscache_wait_for_operation_activation( +		object, &op->op,  		__fscache_stat(&fscache_n_alloc_op_waits), -		__fscache_stat(&fscache_n_allocs_object_dead)); +		__fscache_stat(&fscache_n_allocs_object_dead), +		fscache_do_cancel_retrieval);  	if (ret < 0)  		goto error; @@ -628,9 +707,13 @@ error:  	_leave(" = %d", ret);  	return ret; +nobufs_unlock_dec: +	wake_cookie = __fscache_unuse_cookie(cookie);  nobufs_unlock:  	spin_unlock(&cookie->lock);  	kfree(op); +	if (wake_cookie) +		__fscache_wake_unused_cookie(cookie);  nobufs:  	fscache_stat(&fscache_n_allocs_nobufs);  	_leave(" = -ENOBUFS"); @@ -639,6 +722,22 @@ nobufs:  EXPORT_SYMBOL(__fscache_alloc_page);  /* + * Unmark pages allocate in the readahead code path (via: + * fscache_readpages_or_alloc) after delegating to the base filesystem + */ +void __fscache_readpages_cancel(struct fscache_cookie *cookie, +				struct list_head *pages) +{ +	struct page *page; + +	list_for_each_entry(page, pages, lru) { +		if (PageFsCache(page)) +			__fscache_uncache_page(cookie, page); +	} +} +EXPORT_SYMBOL(__fscache_readpages_cancel); + +/*   * release a write op reference   */  static void fscache_release_write_op(struct fscache_operation *_op) @@ -662,14 +761,31 @@ static void fscache_write_op(struct fscache_operation *_op)  	_enter("{OP%x,%d}", op->op.debug_id, atomic_read(&op->op.usage)); -	fscache_set_op_state(&op->op, "GetPage"); -  	spin_lock(&object->lock);  	cookie = object->cookie; -	if (!fscache_object_is_active(object) || !cookie) { +	if (!fscache_object_is_active(object)) { +		/* If we get here, then the on-disk cache object likely longer +		 * exists, so we should just cancel this write operation. +		 */  		spin_unlock(&object->lock); -		_leave(""); +		fscache_op_complete(&op->op, false); +		_leave(" [inactive]"); +		return; +	} + +	if (!cookie) { +		/* If we get here, then the cookie belonging to the object was +		 * detached, probably by the cookie being withdrawn due to +		 * memory pressure, which means that the pages we might write +		 * to the cache from no longer exist - therefore, we can just +		 * cancel this write operation. +		 */ +		spin_unlock(&object->lock); +		fscache_op_complete(&op->op, false); +		_leave(" [cancel] op{f=%lx s=%u} obj{s=%s f=%lx}", +		       _op->flags, _op->state, object->state->short_name, +		       object->flags);  		return;  	} @@ -698,16 +814,14 @@ static void fscache_write_op(struct fscache_operation *_op)  	spin_unlock(&cookie->stores_lock);  	spin_unlock(&object->lock); -	fscache_set_op_state(&op->op, "Store");  	fscache_stat(&fscache_n_store_pages);  	fscache_stat(&fscache_n_cop_write_page);  	ret = object->cache->ops->write_page(op, page);  	fscache_stat_d(&fscache_n_cop_write_page); -	fscache_set_op_state(&op->op, "EndWrite");  	fscache_end_page_write(object, page);  	if (ret < 0) { -		fscache_set_op_state(&op->op, "Abort");  		fscache_abort_object(object); +		fscache_op_complete(&op->op, true);  	} else {  		fscache_enqueue_operation(&op->op);  	} @@ -722,6 +836,42 @@ superseded:  	spin_unlock(&cookie->stores_lock);  	clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);  	spin_unlock(&object->lock); +	fscache_op_complete(&op->op, true); +	_leave(""); +} + +/* + * Clear the pages pending writing for invalidation + */ +void fscache_invalidate_writes(struct fscache_cookie *cookie) +{ +	struct page *page; +	void *results[16]; +	int n, i; + +	_enter(""); + +	for (;;) { +		spin_lock(&cookie->stores_lock); +		n = radix_tree_gang_lookup_tag(&cookie->stores, results, 0, +					       ARRAY_SIZE(results), +					       FSCACHE_COOKIE_PENDING_TAG); +		if (n == 0) { +			spin_unlock(&cookie->stores_lock); +			break; +		} + +		for (i = n - 1; i >= 0; i--) { +			page = results[i]; +			radix_tree_delete(&cookie->stores, page->index); +		} + +		spin_unlock(&cookie->stores_lock); + +		for (i = n - 1; i >= 0; i--) +			page_cache_release(results[i]); +	} +  	_leave("");  } @@ -738,14 +888,12 @@ superseded:   *  (1) negative lookup, object not yet created (FSCACHE_COOKIE_CREATING is   *      set)   * - *	(a) no writes yet (set FSCACHE_COOKIE_PENDING_FILL and queue deferred - *	    fill op) + *	(a) no writes yet   *   *	(b) writes deferred till post-creation (mark page for writing and   *	    return immediately)   *   *  (2) negative lookup, object created, initial fill being made from netfs - *      (FSCACHE_COOKIE_INITIAL_FILL is set)   *   *	(a) fill point not yet reached this page (mark page for writing and   *          return) @@ -762,6 +910,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,  {  	struct fscache_storage *op;  	struct fscache_object *object; +	bool wake_cookie = false;  	int ret;  	_enter("%p,%x,", cookie, (u32) page->flags); @@ -771,23 +920,30 @@ int __fscache_write_page(struct fscache_cookie *cookie,  	fscache_stat(&fscache_n_stores); -	op = kzalloc(sizeof(*op), GFP_NOIO); +	if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { +		_leave(" = -ENOBUFS [invalidating]"); +		return -ENOBUFS; +	} + +	op = kzalloc(sizeof(*op), GFP_NOIO | __GFP_NOMEMALLOC | __GFP_NORETRY);  	if (!op)  		goto nomem;  	fscache_operation_init(&op->op, fscache_write_op,  			       fscache_release_write_op); -	op->op.flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_WAITING); -	fscache_set_op_name(&op->op, "Write1"); +	op->op.flags = FSCACHE_OP_ASYNC | +		(1 << FSCACHE_OP_WAITING) | +		(1 << FSCACHE_OP_UNUSE_COOKIE); -	ret = radix_tree_preload(gfp & ~__GFP_HIGHMEM); +	ret = radix_tree_maybe_preload(gfp & ~__GFP_HIGHMEM);  	if (ret < 0)  		goto nomem_free;  	ret = -ENOBUFS;  	spin_lock(&cookie->lock); -	if (hlist_empty(&cookie->backing_objects)) +	if (!fscache_cookie_enabled(cookie) || +	    hlist_empty(&cookie->backing_objects))  		goto nobufs;  	object = hlist_entry(cookie->backing_objects.first,  			     struct fscache_object, cookie_link); @@ -824,6 +980,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,  	op->op.debug_id	= atomic_inc_return(&fscache_op_debug_id);  	op->store_limit = object->store_limit; +	__fscache_use_cookie(cookie);  	if (fscache_submit_op(object, &op->op) < 0)  		goto submit_failed; @@ -853,6 +1010,7 @@ submit_failed:  	spin_lock(&cookie->stores_lock);  	radix_tree_delete(&cookie->stores, page->index);  	spin_unlock(&cookie->stores_lock); +	wake_cookie = __fscache_unuse_cookie(cookie);  	page_cache_release(page);  	ret = -ENOBUFS;  	goto nobufs; @@ -864,6 +1022,8 @@ nobufs:  	spin_unlock(&cookie->lock);  	radix_tree_preload_end();  	kfree(op); +	if (wake_cookie) +		__fscache_wake_unused_cookie(cookie);  	fscache_stat(&fscache_n_stores_nobufs);  	_leave(" = -ENOBUFS");  	return -ENOBUFS; @@ -928,6 +1088,38 @@ done:  EXPORT_SYMBOL(__fscache_uncache_page);  /** + * fscache_mark_page_cached - Mark a page as being cached + * @op: The retrieval op pages are being marked for + * @page: The page to be marked + * + * Mark a netfs page as being cached.  After this is called, the netfs + * must call fscache_uncache_page() to remove the mark. + */ +void fscache_mark_page_cached(struct fscache_retrieval *op, struct page *page) +{ +	struct fscache_cookie *cookie = op->op.object->cookie; + +#ifdef CONFIG_FSCACHE_STATS +	atomic_inc(&fscache_n_marks); +#endif + +	_debug("- mark %p{%lx}", page, page->index); +	if (TestSetPageFsCache(page)) { +		static bool once_only; +		if (!once_only) { +			once_only = true; +			pr_warn("Cookie type %s marked page %lx multiple times\n", +				cookie->def->name, page->index); +		} +	} + +	if (cookie->def->mark_page_cached) +		cookie->def->mark_page_cached(cookie->netfs_data, +					      op->mapping, page); +} +EXPORT_SYMBOL(fscache_mark_page_cached); + +/**   * fscache_mark_pages_cached - Mark pages as being cached   * @op: The retrieval op pages are being marked for   * @pagevec: The pages to be marked @@ -938,32 +1130,51 @@ EXPORT_SYMBOL(__fscache_uncache_page);  void fscache_mark_pages_cached(struct fscache_retrieval *op,  			       struct pagevec *pagevec)  { -	struct fscache_cookie *cookie = op->op.object->cookie;  	unsigned long loop; -#ifdef CONFIG_FSCACHE_STATS -	atomic_add(pagevec->nr, &fscache_n_marks); -#endif +	for (loop = 0; loop < pagevec->nr; loop++) +		fscache_mark_page_cached(op, pagevec->pages[loop]); + +	pagevec_reinit(pagevec); +} +EXPORT_SYMBOL(fscache_mark_pages_cached); -	for (loop = 0; loop < pagevec->nr; loop++) { -		struct page *page = pagevec->pages[loop]; - -		_debug("- mark %p{%lx}", page, page->index); -		if (TestSetPageFsCache(page)) { -			static bool once_only; -			if (!once_only) { -				once_only = true; -				printk(KERN_WARNING "FS-Cache:" -				       " Cookie type %s marked page %lx" -				       " multiple times\n", -				       cookie->def->name, page->index); +/* + * Uncache all the pages in an inode that are marked PG_fscache, assuming them + * to be associated with the given cookie. + */ +void __fscache_uncache_all_inode_pages(struct fscache_cookie *cookie, +				       struct inode *inode) +{ +	struct address_space *mapping = inode->i_mapping; +	struct pagevec pvec; +	pgoff_t next; +	int i; + +	_enter("%p,%p", cookie, inode); + +	if (!mapping || mapping->nrpages == 0) { +		_leave(" [no pages]"); +		return; +	} + +	pagevec_init(&pvec, 0); +	next = 0; +	do { +		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) +			break; +		for (i = 0; i < pagevec_count(&pvec); i++) { +			struct page *page = pvec.pages[i]; +			next = page->index; +			if (PageFsCache(page)) { +				__fscache_wait_on_page_write(cookie, page); +				__fscache_uncache_page(cookie, page);  			}  		} -	} +		pagevec_release(&pvec); +		cond_resched(); +	} while (++next); -	if (cookie->def->mark_pages_cached) -		cookie->def->mark_pages_cached(cookie->netfs_data, -					       op->mapping, pagevec); -	pagevec_reinit(pagevec); +	_leave("");  } -EXPORT_SYMBOL(fscache_mark_pages_cached); +EXPORT_SYMBOL(__fscache_uncache_all_inode_pages); diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index 4765190d537..40d13c70ef5 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -69,6 +69,7 @@ atomic_t fscache_n_store_vmscan_not_storing;  atomic_t fscache_n_store_vmscan_gone;  atomic_t fscache_n_store_vmscan_busy;  atomic_t fscache_n_store_vmscan_cancelled; +atomic_t fscache_n_store_vmscan_wait;  atomic_t fscache_n_marks;  atomic_t fscache_n_uncaches; @@ -80,6 +81,9 @@ atomic_t fscache_n_acquires_ok;  atomic_t fscache_n_acquires_nobufs;  atomic_t fscache_n_acquires_oom; +atomic_t fscache_n_invalidates; +atomic_t fscache_n_invalidates_run; +  atomic_t fscache_n_updates;  atomic_t fscache_n_updates_null;  atomic_t fscache_n_updates_run; @@ -112,6 +116,7 @@ atomic_t fscache_n_cop_alloc_object;  atomic_t fscache_n_cop_lookup_object;  atomic_t fscache_n_cop_lookup_complete;  atomic_t fscache_n_cop_grab_object; +atomic_t fscache_n_cop_invalidate_object;  atomic_t fscache_n_cop_update_object;  atomic_t fscache_n_cop_drop_object;  atomic_t fscache_n_cop_put_object; @@ -168,6 +173,10 @@ static int fscache_stats_show(struct seq_file *m, void *v)  		   atomic_read(&fscache_n_object_created),  		   atomic_read(&fscache_n_object_lookups_timed_out)); +	seq_printf(m, "Invals : n=%u run=%u\n", +		   atomic_read(&fscache_n_invalidates), +		   atomic_read(&fscache_n_invalidates_run)); +  	seq_printf(m, "Updates: n=%u nul=%u run=%u\n",  		   atomic_read(&fscache_n_updates),  		   atomic_read(&fscache_n_updates_null), @@ -224,11 +233,12 @@ static int fscache_stats_show(struct seq_file *m, void *v)  		   atomic_read(&fscache_n_store_radix_deletes),  		   atomic_read(&fscache_n_store_pages_over_limit)); -	seq_printf(m, "VmScan : nos=%u gon=%u bsy=%u can=%u\n", +	seq_printf(m, "VmScan : nos=%u gon=%u bsy=%u can=%u wt=%u\n",  		   atomic_read(&fscache_n_store_vmscan_not_storing),  		   atomic_read(&fscache_n_store_vmscan_gone),  		   atomic_read(&fscache_n_store_vmscan_busy), -		   atomic_read(&fscache_n_store_vmscan_cancelled)); +		   atomic_read(&fscache_n_store_vmscan_cancelled), +		   atomic_read(&fscache_n_store_vmscan_wait));  	seq_printf(m, "Ops    : pend=%u run=%u enq=%u can=%u rej=%u\n",  		   atomic_read(&fscache_n_op_pend), @@ -246,7 +256,8 @@ static int fscache_stats_show(struct seq_file *m, void *v)  		   atomic_read(&fscache_n_cop_lookup_object),  		   atomic_read(&fscache_n_cop_lookup_complete),  		   atomic_read(&fscache_n_cop_grab_object)); -	seq_printf(m, "CacheOp: upo=%d dro=%d pto=%d atc=%d syn=%d\n", +	seq_printf(m, "CacheOp: inv=%d upo=%d dro=%d pto=%d atc=%d syn=%d\n", +		   atomic_read(&fscache_n_cop_invalidate_object),  		   atomic_read(&fscache_n_cop_update_object),  		   atomic_read(&fscache_n_cop_drop_object),  		   atomic_read(&fscache_n_cop_put_object), @@ -276,5 +287,5 @@ const struct file_operations fscache_stats_fops = {  	.open		= fscache_stats_open,  	.read		= seq_read,  	.llseek		= seq_lseek, -	.release	= seq_release, +	.release        = single_release,  };  | 
