diff options
Diffstat (limited to 'fs/fscache/cookie.c')
| -rw-r--r-- | fs/fscache/cookie.c | 369 | 
1 files changed, 289 insertions, 80 deletions
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);  | 
