diff options
Diffstat (limited to 'fs/fscache/page.c')
| -rw-r--r-- | fs/fscache/page.c | 273 | 
1 files changed, 224 insertions, 49 deletions
diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 2568e0eb644..c598ea4c4e7 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -43,18 +43,102 @@ void __fscache_wait_on_page_write(struct fscache_cookie *cookie, struct page *pa  EXPORT_SYMBOL(__fscache_wait_on_page_write);  /* - * note that a page has finished being written to the cache + * decide whether a page can be released, possibly by cancelling a store to it + * - we're allowed to sleep if __GFP_WAIT is flagged   */ -static void fscache_end_page_write(struct fscache_cookie *cookie, struct page *page) +bool __fscache_maybe_release_page(struct fscache_cookie *cookie, +				  struct page *page, +				  gfp_t gfp)  {  	struct page *xpage; +	void *val; + +	_enter("%p,%p,%x", cookie, page, gfp); + +	rcu_read_lock(); +	val = radix_tree_lookup(&cookie->stores, page->index); +	if (!val) { +		rcu_read_unlock(); +		fscache_stat(&fscache_n_store_vmscan_not_storing); +		__fscache_uncache_page(cookie, page); +		return true; +	} + +	/* see if the page is actually undergoing storage - if so we can't get +	 * rid of it till the cache has finished with it */ +	if (radix_tree_tag_get(&cookie->stores, page->index, +			       FSCACHE_COOKIE_STORING_TAG)) { +		rcu_read_unlock(); +		goto page_busy; +	} + +	/* the page is pending storage, so we attempt to cancel the store and +	 * discard the store request so that the page can be reclaimed */ +	spin_lock(&cookie->stores_lock); +	rcu_read_unlock(); + +	if (radix_tree_tag_get(&cookie->stores, page->index, +			       FSCACHE_COOKIE_STORING_TAG)) { +		/* the page started to undergo storage whilst we were looking, +		 * so now we can only wait or return */ +		spin_unlock(&cookie->stores_lock); +		goto page_busy; +	} -	spin_lock(&cookie->lock);  	xpage = radix_tree_delete(&cookie->stores, page->index); -	spin_unlock(&cookie->lock); -	ASSERT(xpage != NULL); +	spin_unlock(&cookie->stores_lock); + +	if (xpage) { +		fscache_stat(&fscache_n_store_vmscan_cancelled); +		fscache_stat(&fscache_n_store_radix_deletes); +		ASSERTCMP(xpage, ==, page); +	} else { +		fscache_stat(&fscache_n_store_vmscan_gone); +	}  	wake_up_bit(&cookie->flags, 0); +	if (xpage) +		page_cache_release(xpage); +	__fscache_uncache_page(cookie, page); +	return true; + +page_busy: +	/* we might want to wait here, but that could deadlock the allocator as +	 * the slow-work threads writing to the cache may all end up sleeping +	 * on memory allocation */ +	fscache_stat(&fscache_n_store_vmscan_busy); +	return false; +} +EXPORT_SYMBOL(__fscache_maybe_release_page); + +/* + * note that a page has finished being written to the cache + */ +static void fscache_end_page_write(struct fscache_object *object, +				   struct page *page) +{ +	struct fscache_cookie *cookie; +	struct page *xpage = NULL; + +	spin_lock(&object->lock); +	cookie = object->cookie; +	if (cookie) { +		/* delete the page from the tree if it is now no longer +		 * pending */ +		spin_lock(&cookie->stores_lock); +		radix_tree_tag_clear(&cookie->stores, page->index, +				     FSCACHE_COOKIE_STORING_TAG); +		if (!radix_tree_tag_get(&cookie->stores, page->index, +					FSCACHE_COOKIE_PENDING_TAG)) { +			fscache_stat(&fscache_n_store_radix_deletes); +			xpage = radix_tree_delete(&cookie->stores, page->index); +		} +		spin_unlock(&cookie->stores_lock); +		wake_up_bit(&cookie->flags, 0); +	} +	spin_unlock(&object->lock); +	if (xpage) +		page_cache_release(xpage);  }  /* @@ -63,14 +147,21 @@ static void fscache_end_page_write(struct fscache_cookie *cookie, struct page *p  static void fscache_attr_changed_op(struct fscache_operation *op)  {  	struct fscache_object *object = op->object; +	int ret;  	_enter("{OBJ%x OP%x}", object->debug_id, op->debug_id);  	fscache_stat(&fscache_n_attr_changed_calls); -	if (fscache_object_is_active(object) && -	    object->cache->ops->attr_changed(object) < 0) -		fscache_abort_object(object); +	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); +	}  	_leave("");  } @@ -99,6 +190,7 @@ int __fscache_attr_changed(struct fscache_cookie *cookie)  	fscache_operation_init(op, NULL);  	fscache_operation_init_slow(op, fscache_attr_changed_op);  	op->flags = FSCACHE_OP_SLOW | (1 << FSCACHE_OP_EXCLUSIVE); +	fscache_set_op_name(op, "Attr");  	spin_lock(&cookie->lock); @@ -184,6 +276,7 @@ static struct fscache_retrieval *fscache_alloc_retrieval(  	op->start_time	= jiffies;  	INIT_WORK(&op->op.fast_work, fscache_retrieval_work);  	INIT_LIST_HEAD(&op->to_do); +	fscache_set_op_name(&op->op, "Retr");  	return op;  } @@ -221,6 +314,43 @@ static int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie)  }  /* + * 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 ret; + +	if (!test_bit(FSCACHE_OP_WAITING, &op->op.flags)) +		goto check_if_dead; + +	_debug(">>> WT"); +	fscache_stat(stat_op_waits); +	if (wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING, +			fscache_wait_bit_interruptible, +			TASK_INTERRUPTIBLE) < 0) { +		ret = fscache_cancel_op(&op->op); +		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, +			    fscache_wait_bit, TASK_UNINTERRUPTIBLE); +	} +	_debug("<<< GO"); + +check_if_dead: +	if (unlikely(fscache_object_is_dead(object))) { +		fscache_stat(stat_object_dead); +		return -ENOBUFS; +	} +	return 0; +} + +/*   * read a page from the cache or allocate a block in which to store it   * - we return:   *   -ENOMEM	- out of memory, nothing done @@ -257,6 +387,7 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,  		_leave(" = -ENOMEM");  		return -ENOMEM;  	} +	fscache_set_op_name(&op->op, "RetrRA1");  	spin_lock(&cookie->lock); @@ -267,6 +398,9 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,  	ASSERTCMP(object->state, >, FSCACHE_OBJECT_LOOKING_UP); +	atomic_inc(&object->n_reads); +	set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); +  	if (fscache_submit_op(object, &op->op) < 0)  		goto nobufs_unlock;  	spin_unlock(&cookie->lock); @@ -279,23 +413,27 @@ 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 */ -	if (test_bit(FSCACHE_OP_WAITING, &op->op.flags)) { -		_debug(">>> WT"); -		fscache_stat(&fscache_n_retrieval_op_waits); -		wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING, -			    fscache_wait_bit, TASK_UNINTERRUPTIBLE); -		_debug("<<< GO"); -	} +	ret = fscache_wait_for_retrieval_activation( +		object, op, +		__fscache_stat(&fscache_n_retrieval_op_waits), +		__fscache_stat(&fscache_n_retrievals_object_dead)); +	if (ret < 0) +		goto error;  	/* ask the cache to honour the operation */  	if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) { +		fscache_stat(&fscache_n_cop_allocate_page);  		ret = object->cache->ops->allocate_page(op, page, gfp); +		fscache_stat_d(&fscache_n_cop_allocate_page);  		if (ret == 0)  			ret = -ENODATA;  	} else { +		fscache_stat(&fscache_n_cop_read_or_alloc_page);  		ret = object->cache->ops->read_or_alloc_page(op, page, gfp); +		fscache_stat_d(&fscache_n_cop_read_or_alloc_page);  	} +error:  	if (ret == -ENOMEM)  		fscache_stat(&fscache_n_retrievals_nomem);  	else if (ret == -ERESTARTSYS) @@ -347,7 +485,6 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  				  void *context,  				  gfp_t gfp)  { -	fscache_pages_retrieval_func_t func;  	struct fscache_retrieval *op;  	struct fscache_object *object;  	int ret; @@ -369,6 +506,7 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  	op = fscache_alloc_retrieval(mapping, end_io_func, context);  	if (!op)  		return -ENOMEM; +	fscache_set_op_name(&op->op, "RetrRAN");  	spin_lock(&cookie->lock); @@ -377,6 +515,9 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,  	object = hlist_entry(cookie->backing_objects.first,  			     struct fscache_object, cookie_link); +	atomic_inc(&object->n_reads); +	set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); +  	if (fscache_submit_op(object, &op->op) < 0)  		goto nobufs_unlock;  	spin_unlock(&cookie->lock); @@ -389,21 +530,27 @@ 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 */ -	if (test_bit(FSCACHE_OP_WAITING, &op->op.flags)) { -		_debug(">>> WT"); -		fscache_stat(&fscache_n_retrieval_op_waits); -		wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING, -			    fscache_wait_bit, TASK_UNINTERRUPTIBLE); -		_debug("<<< GO"); -	} +	ret = fscache_wait_for_retrieval_activation( +		object, op, +		__fscache_stat(&fscache_n_retrieval_op_waits), +		__fscache_stat(&fscache_n_retrievals_object_dead)); +	if (ret < 0) +		goto error;  	/* ask the cache to honour the operation */ -	if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) -		func = object->cache->ops->allocate_pages; -	else -		func = object->cache->ops->read_or_alloc_pages; -	ret = func(op, pages, nr_pages, gfp); +	if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) { +		fscache_stat(&fscache_n_cop_allocate_pages); +		ret = object->cache->ops->allocate_pages( +			op, pages, nr_pages, gfp); +		fscache_stat_d(&fscache_n_cop_allocate_pages); +	} else { +		fscache_stat(&fscache_n_cop_read_or_alloc_pages); +		ret = object->cache->ops->read_or_alloc_pages( +			op, pages, nr_pages, gfp); +		fscache_stat_d(&fscache_n_cop_read_or_alloc_pages); +	} +error:  	if (ret == -ENOMEM)  		fscache_stat(&fscache_n_retrievals_nomem);  	else if (ret == -ERESTARTSYS) @@ -461,6 +608,7 @@ int __fscache_alloc_page(struct fscache_cookie *cookie,  	op = fscache_alloc_retrieval(page->mapping, NULL, NULL);  	if (!op)  		return -ENOMEM; +	fscache_set_op_name(&op->op, "RetrAL1");  	spin_lock(&cookie->lock); @@ -475,18 +623,22 @@ int __fscache_alloc_page(struct fscache_cookie *cookie,  	fscache_stat(&fscache_n_alloc_ops); -	if (test_bit(FSCACHE_OP_WAITING, &op->op.flags)) { -		_debug(">>> WT"); -		fscache_stat(&fscache_n_alloc_op_waits); -		wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING, -			    fscache_wait_bit, TASK_UNINTERRUPTIBLE); -		_debug("<<< GO"); -	} +	ret = fscache_wait_for_retrieval_activation( +		object, op, +		__fscache_stat(&fscache_n_alloc_op_waits), +		__fscache_stat(&fscache_n_allocs_object_dead)); +	if (ret < 0) +		goto error;  	/* ask the cache to honour the operation */ +	fscache_stat(&fscache_n_cop_allocate_page);  	ret = object->cache->ops->allocate_page(op, page, gfp); +	fscache_stat_d(&fscache_n_cop_allocate_page); -	if (ret < 0) +error: +	if (ret == -ERESTARTSYS) +		fscache_stat(&fscache_n_allocs_intr); +	else if (ret < 0)  		fscache_stat(&fscache_n_allocs_nobufs);  	else  		fscache_stat(&fscache_n_allocs_ok); @@ -521,7 +673,7 @@ static void fscache_write_op(struct fscache_operation *_op)  	struct fscache_storage *op =  		container_of(_op, struct fscache_storage, op);  	struct fscache_object *object = op->op.object; -	struct fscache_cookie *cookie = object->cookie; +	struct fscache_cookie *cookie;  	struct page *page;  	unsigned n;  	void *results[1]; @@ -529,16 +681,19 @@ static void fscache_write_op(struct fscache_operation *_op)  	_enter("{OP%x,%d}", op->op.debug_id, atomic_read(&op->op.usage)); -	spin_lock(&cookie->lock); +	fscache_set_op_state(&op->op, "GetPage"); +  	spin_lock(&object->lock); +	cookie = object->cookie; -	if (!fscache_object_is_active(object)) { +	if (!fscache_object_is_active(object) || !cookie) {  		spin_unlock(&object->lock); -		spin_unlock(&cookie->lock);  		_leave("");  		return;  	} +	spin_lock(&cookie->stores_lock); +  	fscache_stat(&fscache_n_store_calls);  	/* find a page to store */ @@ -549,23 +704,35 @@ static void fscache_write_op(struct fscache_operation *_op)  		goto superseded;  	page = results[0];  	_debug("gang %d [%lx]", n, page->index); -	if (page->index > op->store_limit) +	if (page->index > op->store_limit) { +		fscache_stat(&fscache_n_store_pages_over_limit);  		goto superseded; +	} -	radix_tree_tag_clear(&cookie->stores, page->index, -			     FSCACHE_COOKIE_PENDING_TAG); +	if (page) { +		radix_tree_tag_set(&cookie->stores, page->index, +				   FSCACHE_COOKIE_STORING_TAG); +		radix_tree_tag_clear(&cookie->stores, page->index, +				     FSCACHE_COOKIE_PENDING_TAG); +	} +	spin_unlock(&cookie->stores_lock);  	spin_unlock(&object->lock); -	spin_unlock(&cookie->lock);  	if (page) { +		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_end_page_write(cookie, page); -		page_cache_release(page); -		if (ret < 0) +		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); -		else +		} else {  			fscache_enqueue_operation(&op->op); +		}  	}  	_leave(""); @@ -575,9 +742,9 @@ superseded:  	/* this writer is going away and there aren't any more things to  	 * write */  	_debug("cease"); +	spin_unlock(&cookie->stores_lock);  	clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);  	spin_unlock(&object->lock); -	spin_unlock(&cookie->lock);  	_leave("");  } @@ -634,6 +801,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,  	fscache_operation_init(&op->op, fscache_release_write_op);  	fscache_operation_init_slow(&op->op, fscache_write_op);  	op->op.flags = FSCACHE_OP_SLOW | (1 << FSCACHE_OP_WAITING); +	fscache_set_op_name(&op->op, "Write1");  	ret = radix_tree_preload(gfp & ~__GFP_HIGHMEM);  	if (ret < 0) @@ -652,6 +820,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,  	/* add the page to the pending-storage radix tree on the backing  	 * object */  	spin_lock(&object->lock); +	spin_lock(&cookie->stores_lock);  	_debug("store limit %llx", (unsigned long long) object->store_limit); @@ -672,6 +841,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,  	if (test_and_set_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags))  		goto already_pending; +	spin_unlock(&cookie->stores_lock);  	spin_unlock(&object->lock);  	op->op.debug_id	= atomic_inc_return(&fscache_op_debug_id); @@ -693,6 +863,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,  already_queued:  	fscache_stat(&fscache_n_stores_again);  already_pending: +	spin_unlock(&cookie->stores_lock);  	spin_unlock(&object->lock);  	spin_unlock(&cookie->lock);  	radix_tree_preload_end(); @@ -702,7 +873,9 @@ already_pending:  	return 0;  submit_failed: +	spin_lock(&cookie->stores_lock);  	radix_tree_delete(&cookie->stores, page->index); +	spin_unlock(&cookie->stores_lock);  	page_cache_release(page);  	ret = -ENOBUFS;  	goto nobufs; @@ -763,7 +936,9 @@ void __fscache_uncache_page(struct fscache_cookie *cookie, struct page *page)  	if (TestClearPageFsCache(page) &&  	    object->cache->ops->uncache_page) {  		/* the cache backend releases the cookie lock */ +		fscache_stat(&fscache_n_cop_uncache_page);  		object->cache->ops->uncache_page(object, page); +		fscache_stat_d(&fscache_n_cop_uncache_page);  		goto done;  	}  |