18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Storage object read/write
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/mount.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/file.h>
118c2ecf20Sopenharmony_ci#include <linux/swap.h>
128c2ecf20Sopenharmony_ci#include "internal.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * detect wake up events generated by the unlocking of pages in which we're
168c2ecf20Sopenharmony_ci * interested
178c2ecf20Sopenharmony_ci * - we use this to detect read completion of backing pages
188c2ecf20Sopenharmony_ci * - the caller holds the waitqueue lock
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_cistatic int cachefiles_read_waiter(wait_queue_entry_t *wait, unsigned mode,
218c2ecf20Sopenharmony_ci				  int sync, void *_key)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct cachefiles_one_read *monitor =
248c2ecf20Sopenharmony_ci		container_of(wait, struct cachefiles_one_read, monitor);
258c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
268c2ecf20Sopenharmony_ci	struct fscache_retrieval *op = monitor->op;
278c2ecf20Sopenharmony_ci	struct wait_page_key *key = _key;
288c2ecf20Sopenharmony_ci	struct page *page = wait->private;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	ASSERT(key);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	_enter("{%lu},%u,%d,{%p,%u}",
338c2ecf20Sopenharmony_ci	       monitor->netfs_page->index, mode, sync,
348c2ecf20Sopenharmony_ci	       key->page, key->bit_nr);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (key->page != page || key->bit_nr != PG_locked)
378c2ecf20Sopenharmony_ci		return 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	_debug("--- monitor %p %lx ---", page, page->flags);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (!PageUptodate(page) && !PageError(page)) {
428c2ecf20Sopenharmony_ci		/* unlocked, not uptodate and not erronous? */
438c2ecf20Sopenharmony_ci		_debug("page probably truncated");
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* remove from the waitqueue */
478c2ecf20Sopenharmony_ci	list_del(&wait->entry);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* move onto the action list and queue for FS-Cache thread pool */
508c2ecf20Sopenharmony_ci	ASSERT(op);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* We need to temporarily bump the usage count as we don't own a ref
538c2ecf20Sopenharmony_ci	 * here otherwise cachefiles_read_copier() may free the op between the
548c2ecf20Sopenharmony_ci	 * monitor being enqueued on the op->to_do list and the op getting
558c2ecf20Sopenharmony_ci	 * enqueued on the work queue.
568c2ecf20Sopenharmony_ci	 */
578c2ecf20Sopenharmony_ci	fscache_get_retrieval(op);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	object = container_of(op->op.object, struct cachefiles_object, fscache);
608c2ecf20Sopenharmony_ci	spin_lock(&object->work_lock);
618c2ecf20Sopenharmony_ci	list_add_tail(&monitor->op_link, &op->to_do);
628c2ecf20Sopenharmony_ci	fscache_enqueue_retrieval(op);
638c2ecf20Sopenharmony_ci	spin_unlock(&object->work_lock);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	fscache_put_retrieval(op);
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * handle a probably truncated page
718c2ecf20Sopenharmony_ci * - check to see if the page is still relevant and reissue the read if
728c2ecf20Sopenharmony_ci *   possible
738c2ecf20Sopenharmony_ci * - return -EIO on error, -ENODATA if the page is gone, -EINPROGRESS if we
748c2ecf20Sopenharmony_ci *   must wait again and 0 if successful
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_cistatic int cachefiles_read_reissue(struct cachefiles_object *object,
778c2ecf20Sopenharmony_ci				   struct cachefiles_one_read *monitor)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct address_space *bmapping = d_backing_inode(object->backer)->i_mapping;
808c2ecf20Sopenharmony_ci	struct page *backpage = monitor->back_page, *backpage2;
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	_enter("{ino=%lx},{%lx,%lx}",
848c2ecf20Sopenharmony_ci	       d_backing_inode(object->backer)->i_ino,
858c2ecf20Sopenharmony_ci	       backpage->index, backpage->flags);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* skip if the page was truncated away completely */
888c2ecf20Sopenharmony_ci	if (backpage->mapping != bmapping) {
898c2ecf20Sopenharmony_ci		_leave(" = -ENODATA [mapping]");
908c2ecf20Sopenharmony_ci		return -ENODATA;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	backpage2 = find_get_page(bmapping, backpage->index);
948c2ecf20Sopenharmony_ci	if (!backpage2) {
958c2ecf20Sopenharmony_ci		_leave(" = -ENODATA [gone]");
968c2ecf20Sopenharmony_ci		return -ENODATA;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (backpage != backpage2) {
1008c2ecf20Sopenharmony_ci		put_page(backpage2);
1018c2ecf20Sopenharmony_ci		_leave(" = -ENODATA [different]");
1028c2ecf20Sopenharmony_ci		return -ENODATA;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* the page is still there and we already have a ref on it, so we don't
1068c2ecf20Sopenharmony_ci	 * need a second */
1078c2ecf20Sopenharmony_ci	put_page(backpage2);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&monitor->op_link);
1108c2ecf20Sopenharmony_ci	add_page_wait_queue(backpage, &monitor->monitor);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (trylock_page(backpage)) {
1138c2ecf20Sopenharmony_ci		ret = -EIO;
1148c2ecf20Sopenharmony_ci		if (PageError(backpage))
1158c2ecf20Sopenharmony_ci			goto unlock_discard;
1168c2ecf20Sopenharmony_ci		ret = 0;
1178c2ecf20Sopenharmony_ci		if (PageUptodate(backpage))
1188c2ecf20Sopenharmony_ci			goto unlock_discard;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		_debug("reissue read");
1218c2ecf20Sopenharmony_ci		ret = bmapping->a_ops->readpage(NULL, backpage);
1228c2ecf20Sopenharmony_ci		if (ret < 0)
1238c2ecf20Sopenharmony_ci			goto discard;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* but the page may have been read before the monitor was installed, so
1278c2ecf20Sopenharmony_ci	 * the monitor may miss the event - so we have to ensure that we do get
1288c2ecf20Sopenharmony_ci	 * one in such a case */
1298c2ecf20Sopenharmony_ci	if (trylock_page(backpage)) {
1308c2ecf20Sopenharmony_ci		_debug("jumpstart %p {%lx}", backpage, backpage->flags);
1318c2ecf20Sopenharmony_ci		unlock_page(backpage);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* it'll reappear on the todo list */
1358c2ecf20Sopenharmony_ci	_leave(" = -EINPROGRESS");
1368c2ecf20Sopenharmony_ci	return -EINPROGRESS;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ciunlock_discard:
1398c2ecf20Sopenharmony_ci	unlock_page(backpage);
1408c2ecf20Sopenharmony_cidiscard:
1418c2ecf20Sopenharmony_ci	spin_lock_irq(&object->work_lock);
1428c2ecf20Sopenharmony_ci	list_del(&monitor->op_link);
1438c2ecf20Sopenharmony_ci	spin_unlock_irq(&object->work_lock);
1448c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
1458c2ecf20Sopenharmony_ci	return ret;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * copy data from backing pages to netfs pages to complete a read operation
1508c2ecf20Sopenharmony_ci * - driven by FS-Cache's thread pool
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistatic void cachefiles_read_copier(struct fscache_operation *_op)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct cachefiles_one_read *monitor;
1558c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
1568c2ecf20Sopenharmony_ci	struct fscache_retrieval *op;
1578c2ecf20Sopenharmony_ci	int error, max;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	op = container_of(_op, struct fscache_retrieval, op);
1608c2ecf20Sopenharmony_ci	object = container_of(op->op.object,
1618c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	_enter("{ino=%lu}", d_backing_inode(object->backer)->i_ino);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	max = 8;
1668c2ecf20Sopenharmony_ci	spin_lock_irq(&object->work_lock);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	while (!list_empty(&op->to_do)) {
1698c2ecf20Sopenharmony_ci		monitor = list_entry(op->to_do.next,
1708c2ecf20Sopenharmony_ci				     struct cachefiles_one_read, op_link);
1718c2ecf20Sopenharmony_ci		list_del(&monitor->op_link);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		spin_unlock_irq(&object->work_lock);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		_debug("- copy {%lu}", monitor->back_page->index);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	recheck:
1788c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_COOKIE_INVALIDATING,
1798c2ecf20Sopenharmony_ci			     &object->fscache.cookie->flags)) {
1808c2ecf20Sopenharmony_ci			error = -ESTALE;
1818c2ecf20Sopenharmony_ci		} else if (PageUptodate(monitor->back_page)) {
1828c2ecf20Sopenharmony_ci			copy_highpage(monitor->netfs_page, monitor->back_page);
1838c2ecf20Sopenharmony_ci			fscache_mark_page_cached(monitor->op,
1848c2ecf20Sopenharmony_ci						 monitor->netfs_page);
1858c2ecf20Sopenharmony_ci			error = 0;
1868c2ecf20Sopenharmony_ci		} else if (!PageError(monitor->back_page)) {
1878c2ecf20Sopenharmony_ci			/* the page has probably been truncated */
1888c2ecf20Sopenharmony_ci			error = cachefiles_read_reissue(object, monitor);
1898c2ecf20Sopenharmony_ci			if (error == -EINPROGRESS)
1908c2ecf20Sopenharmony_ci				goto next;
1918c2ecf20Sopenharmony_ci			goto recheck;
1928c2ecf20Sopenharmony_ci		} else {
1938c2ecf20Sopenharmony_ci			cachefiles_io_error_obj(
1948c2ecf20Sopenharmony_ci				object,
1958c2ecf20Sopenharmony_ci				"Readpage failed on backing file %lx",
1968c2ecf20Sopenharmony_ci				(unsigned long) monitor->back_page->flags);
1978c2ecf20Sopenharmony_ci			error = -EIO;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		put_page(monitor->back_page);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		fscache_end_io(op, monitor->netfs_page, error);
2038c2ecf20Sopenharmony_ci		put_page(monitor->netfs_page);
2048c2ecf20Sopenharmony_ci		fscache_retrieval_complete(op, 1);
2058c2ecf20Sopenharmony_ci		fscache_put_retrieval(op);
2068c2ecf20Sopenharmony_ci		kfree(monitor);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	next:
2098c2ecf20Sopenharmony_ci		/* let the thread pool have some air occasionally */
2108c2ecf20Sopenharmony_ci		max--;
2118c2ecf20Sopenharmony_ci		if (max < 0 || need_resched()) {
2128c2ecf20Sopenharmony_ci			if (!list_empty(&op->to_do))
2138c2ecf20Sopenharmony_ci				fscache_enqueue_retrieval(op);
2148c2ecf20Sopenharmony_ci			_leave(" [maxed out]");
2158c2ecf20Sopenharmony_ci			return;
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		spin_lock_irq(&object->work_lock);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	spin_unlock_irq(&object->work_lock);
2228c2ecf20Sopenharmony_ci	_leave("");
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/*
2268c2ecf20Sopenharmony_ci * read the corresponding page to the given set from the backing file
2278c2ecf20Sopenharmony_ci * - an uncertain page is simply discarded, to be tried again another time
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_cistatic int cachefiles_read_backing_file_one(struct cachefiles_object *object,
2308c2ecf20Sopenharmony_ci					    struct fscache_retrieval *op,
2318c2ecf20Sopenharmony_ci					    struct page *netpage)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct cachefiles_one_read *monitor;
2348c2ecf20Sopenharmony_ci	struct address_space *bmapping;
2358c2ecf20Sopenharmony_ci	struct page *newpage, *backpage;
2368c2ecf20Sopenharmony_ci	int ret;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	_enter("");
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	_debug("read back %p{%lu,%d}",
2418c2ecf20Sopenharmony_ci	       netpage, netpage->index, page_count(netpage));
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	monitor = kzalloc(sizeof(*monitor), cachefiles_gfp);
2448c2ecf20Sopenharmony_ci	if (!monitor)
2458c2ecf20Sopenharmony_ci		goto nomem;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	monitor->netfs_page = netpage;
2488c2ecf20Sopenharmony_ci	monitor->op = fscache_get_retrieval(op);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	init_waitqueue_func_entry(&monitor->monitor, cachefiles_read_waiter);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* attempt to get hold of the backing page */
2538c2ecf20Sopenharmony_ci	bmapping = d_backing_inode(object->backer)->i_mapping;
2548c2ecf20Sopenharmony_ci	newpage = NULL;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (;;) {
2578c2ecf20Sopenharmony_ci		backpage = find_get_page(bmapping, netpage->index);
2588c2ecf20Sopenharmony_ci		if (backpage)
2598c2ecf20Sopenharmony_ci			goto backing_page_already_present;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		if (!newpage) {
2628c2ecf20Sopenharmony_ci			newpage = __page_cache_alloc(cachefiles_gfp);
2638c2ecf20Sopenharmony_ci			if (!newpage)
2648c2ecf20Sopenharmony_ci				goto nomem_monitor;
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		ret = add_to_page_cache_lru(newpage, bmapping,
2688c2ecf20Sopenharmony_ci					    netpage->index, cachefiles_gfp);
2698c2ecf20Sopenharmony_ci		if (ret == 0)
2708c2ecf20Sopenharmony_ci			goto installed_new_backing_page;
2718c2ecf20Sopenharmony_ci		if (ret != -EEXIST)
2728c2ecf20Sopenharmony_ci			goto nomem_page;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* we've installed a new backing page, so now we need to start
2768c2ecf20Sopenharmony_ci	 * it reading */
2778c2ecf20Sopenharmony_ciinstalled_new_backing_page:
2788c2ecf20Sopenharmony_ci	_debug("- new %p", newpage);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	backpage = newpage;
2818c2ecf20Sopenharmony_ci	newpage = NULL;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ciread_backing_page:
2848c2ecf20Sopenharmony_ci	ret = bmapping->a_ops->readpage(NULL, backpage);
2858c2ecf20Sopenharmony_ci	if (ret < 0)
2868c2ecf20Sopenharmony_ci		goto read_error;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* set the monitor to transfer the data across */
2898c2ecf20Sopenharmony_cimonitor_backing_page:
2908c2ecf20Sopenharmony_ci	_debug("- monitor add");
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* install the monitor */
2938c2ecf20Sopenharmony_ci	get_page(monitor->netfs_page);
2948c2ecf20Sopenharmony_ci	get_page(backpage);
2958c2ecf20Sopenharmony_ci	monitor->back_page = backpage;
2968c2ecf20Sopenharmony_ci	monitor->monitor.private = backpage;
2978c2ecf20Sopenharmony_ci	add_page_wait_queue(backpage, &monitor->monitor);
2988c2ecf20Sopenharmony_ci	monitor = NULL;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* but the page may have been read before the monitor was installed, so
3018c2ecf20Sopenharmony_ci	 * the monitor may miss the event - so we have to ensure that we do get
3028c2ecf20Sopenharmony_ci	 * one in such a case */
3038c2ecf20Sopenharmony_ci	if (trylock_page(backpage)) {
3048c2ecf20Sopenharmony_ci		_debug("jumpstart %p {%lx}", backpage, backpage->flags);
3058c2ecf20Sopenharmony_ci		unlock_page(backpage);
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	goto success;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* if the backing page is already present, it can be in one of
3108c2ecf20Sopenharmony_ci	 * three states: read in progress, read failed or read okay */
3118c2ecf20Sopenharmony_cibacking_page_already_present:
3128c2ecf20Sopenharmony_ci	_debug("- present");
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (newpage) {
3158c2ecf20Sopenharmony_ci		put_page(newpage);
3168c2ecf20Sopenharmony_ci		newpage = NULL;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (PageError(backpage))
3208c2ecf20Sopenharmony_ci		goto io_error;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (PageUptodate(backpage))
3238c2ecf20Sopenharmony_ci		goto backing_page_already_uptodate;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (!trylock_page(backpage))
3268c2ecf20Sopenharmony_ci		goto monitor_backing_page;
3278c2ecf20Sopenharmony_ci	_debug("read %p {%lx}", backpage, backpage->flags);
3288c2ecf20Sopenharmony_ci	goto read_backing_page;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* the backing page is already up to date, attach the netfs
3318c2ecf20Sopenharmony_ci	 * page to the pagecache and LRU and copy the data across */
3328c2ecf20Sopenharmony_cibacking_page_already_uptodate:
3338c2ecf20Sopenharmony_ci	_debug("- uptodate");
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	fscache_mark_page_cached(op, netpage);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	copy_highpage(netpage, backpage);
3388c2ecf20Sopenharmony_ci	fscache_end_io(op, netpage, 0);
3398c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, 1);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cisuccess:
3428c2ecf20Sopenharmony_ci	_debug("success");
3438c2ecf20Sopenharmony_ci	ret = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ciout:
3468c2ecf20Sopenharmony_ci	if (backpage)
3478c2ecf20Sopenharmony_ci		put_page(backpage);
3488c2ecf20Sopenharmony_ci	if (monitor) {
3498c2ecf20Sopenharmony_ci		fscache_put_retrieval(monitor->op);
3508c2ecf20Sopenharmony_ci		kfree(monitor);
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
3538c2ecf20Sopenharmony_ci	return ret;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciread_error:
3568c2ecf20Sopenharmony_ci	_debug("read error %d", ret);
3578c2ecf20Sopenharmony_ci	if (ret == -ENOMEM) {
3588c2ecf20Sopenharmony_ci		fscache_retrieval_complete(op, 1);
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ciio_error:
3628c2ecf20Sopenharmony_ci	cachefiles_io_error_obj(object, "Page read error on backing file");
3638c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, 1);
3648c2ecf20Sopenharmony_ci	ret = -ENOBUFS;
3658c2ecf20Sopenharmony_ci	goto out;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cinomem_page:
3688c2ecf20Sopenharmony_ci	put_page(newpage);
3698c2ecf20Sopenharmony_cinomem_monitor:
3708c2ecf20Sopenharmony_ci	fscache_put_retrieval(monitor->op);
3718c2ecf20Sopenharmony_ci	kfree(monitor);
3728c2ecf20Sopenharmony_cinomem:
3738c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, 1);
3748c2ecf20Sopenharmony_ci	_leave(" = -ENOMEM");
3758c2ecf20Sopenharmony_ci	return -ENOMEM;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/*
3798c2ecf20Sopenharmony_ci * read a page from the cache or allocate a block in which to store it
3808c2ecf20Sopenharmony_ci * - cache withdrawal is prevented by the caller
3818c2ecf20Sopenharmony_ci * - returns -EINTR if interrupted
3828c2ecf20Sopenharmony_ci * - returns -ENOMEM if ran out of memory
3838c2ecf20Sopenharmony_ci * - returns -ENOBUFS if no buffers can be made available
3848c2ecf20Sopenharmony_ci * - returns -ENOBUFS if page is beyond EOF
3858c2ecf20Sopenharmony_ci * - if the page is backed by a block in the cache:
3868c2ecf20Sopenharmony_ci *   - a read will be started which will call the callback on completion
3878c2ecf20Sopenharmony_ci *   - 0 will be returned
3888c2ecf20Sopenharmony_ci * - else if the page is unbacked:
3898c2ecf20Sopenharmony_ci *   - the metadata will be retained
3908c2ecf20Sopenharmony_ci *   - -ENODATA will be returned
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_ciint cachefiles_read_or_alloc_page(struct fscache_retrieval *op,
3938c2ecf20Sopenharmony_ci				  struct page *page,
3948c2ecf20Sopenharmony_ci				  gfp_t gfp)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
3978c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
3988c2ecf20Sopenharmony_ci	struct inode *inode;
3998c2ecf20Sopenharmony_ci	sector_t block;
4008c2ecf20Sopenharmony_ci	unsigned shift;
4018c2ecf20Sopenharmony_ci	int ret, ret2;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	object = container_of(op->op.object,
4048c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
4058c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
4068c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	_enter("{%p},{%lx},,,", object, page->index);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (!object->backer)
4118c2ecf20Sopenharmony_ci		goto enobufs;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	inode = d_backing_inode(object->backer);
4148c2ecf20Sopenharmony_ci	ASSERT(S_ISREG(inode->i_mode));
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* calculate the shift required to use bmap */
4178c2ecf20Sopenharmony_ci	shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	op->op.flags &= FSCACHE_OP_KEEP_FLAGS;
4208c2ecf20Sopenharmony_ci	op->op.flags |= FSCACHE_OP_ASYNC;
4218c2ecf20Sopenharmony_ci	op->op.processor = cachefiles_read_copier;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* we assume the absence or presence of the first block is a good
4248c2ecf20Sopenharmony_ci	 * enough indication for the page as a whole
4258c2ecf20Sopenharmony_ci	 * - TODO: don't use bmap() for this as it is _not_ actually good
4268c2ecf20Sopenharmony_ci	 *   enough for this as it doesn't indicate errors, but it's all we've
4278c2ecf20Sopenharmony_ci	 *   got for the moment
4288c2ecf20Sopenharmony_ci	 */
4298c2ecf20Sopenharmony_ci	block = page->index;
4308c2ecf20Sopenharmony_ci	block <<= shift;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ret2 = bmap(inode, &block);
4338c2ecf20Sopenharmony_ci	ASSERT(ret2 == 0);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	_debug("%llx -> %llx",
4368c2ecf20Sopenharmony_ci	       (unsigned long long) (page->index << shift),
4378c2ecf20Sopenharmony_ci	       (unsigned long long) block);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (block) {
4408c2ecf20Sopenharmony_ci		/* submit the apparently valid page to the backing fs to be
4418c2ecf20Sopenharmony_ci		 * read from disk */
4428c2ecf20Sopenharmony_ci		ret = cachefiles_read_backing_file_one(object, op, page);
4438c2ecf20Sopenharmony_ci	} else if (cachefiles_has_space(cache, 0, 1) == 0) {
4448c2ecf20Sopenharmony_ci		/* there's space in the cache we can use */
4458c2ecf20Sopenharmony_ci		fscache_mark_page_cached(op, page);
4468c2ecf20Sopenharmony_ci		fscache_retrieval_complete(op, 1);
4478c2ecf20Sopenharmony_ci		ret = -ENODATA;
4488c2ecf20Sopenharmony_ci	} else {
4498c2ecf20Sopenharmony_ci		goto enobufs;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
4538c2ecf20Sopenharmony_ci	return ret;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cienobufs:
4568c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, 1);
4578c2ecf20Sopenharmony_ci	_leave(" = -ENOBUFS");
4588c2ecf20Sopenharmony_ci	return -ENOBUFS;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/*
4628c2ecf20Sopenharmony_ci * read the corresponding pages to the given set from the backing file
4638c2ecf20Sopenharmony_ci * - any uncertain pages are simply discarded, to be tried again another time
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_cistatic int cachefiles_read_backing_file(struct cachefiles_object *object,
4668c2ecf20Sopenharmony_ci					struct fscache_retrieval *op,
4678c2ecf20Sopenharmony_ci					struct list_head *list)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct cachefiles_one_read *monitor = NULL;
4708c2ecf20Sopenharmony_ci	struct address_space *bmapping = d_backing_inode(object->backer)->i_mapping;
4718c2ecf20Sopenharmony_ci	struct page *newpage = NULL, *netpage, *_n, *backpage = NULL;
4728c2ecf20Sopenharmony_ci	int ret = 0;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	_enter("");
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	list_for_each_entry_safe(netpage, _n, list, lru) {
4778c2ecf20Sopenharmony_ci		list_del(&netpage->lru);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		_debug("read back %p{%lu,%d}",
4808c2ecf20Sopenharmony_ci		       netpage, netpage->index, page_count(netpage));
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci		if (!monitor) {
4838c2ecf20Sopenharmony_ci			monitor = kzalloc(sizeof(*monitor), cachefiles_gfp);
4848c2ecf20Sopenharmony_ci			if (!monitor)
4858c2ecf20Sopenharmony_ci				goto nomem;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci			monitor->op = fscache_get_retrieval(op);
4888c2ecf20Sopenharmony_ci			init_waitqueue_func_entry(&monitor->monitor,
4898c2ecf20Sopenharmony_ci						  cachefiles_read_waiter);
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		for (;;) {
4938c2ecf20Sopenharmony_ci			backpage = find_get_page(bmapping, netpage->index);
4948c2ecf20Sopenharmony_ci			if (backpage)
4958c2ecf20Sopenharmony_ci				goto backing_page_already_present;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci			if (!newpage) {
4988c2ecf20Sopenharmony_ci				newpage = __page_cache_alloc(cachefiles_gfp);
4998c2ecf20Sopenharmony_ci				if (!newpage)
5008c2ecf20Sopenharmony_ci					goto nomem;
5018c2ecf20Sopenharmony_ci			}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci			ret = add_to_page_cache_lru(newpage, bmapping,
5048c2ecf20Sopenharmony_ci						    netpage->index,
5058c2ecf20Sopenharmony_ci						    cachefiles_gfp);
5068c2ecf20Sopenharmony_ci			if (ret == 0)
5078c2ecf20Sopenharmony_ci				goto installed_new_backing_page;
5088c2ecf20Sopenharmony_ci			if (ret != -EEXIST)
5098c2ecf20Sopenharmony_ci				goto nomem;
5108c2ecf20Sopenharmony_ci		}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		/* we've installed a new backing page, so now we need
5138c2ecf20Sopenharmony_ci		 * to start it reading */
5148c2ecf20Sopenharmony_ci	installed_new_backing_page:
5158c2ecf20Sopenharmony_ci		_debug("- new %p", newpage);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		backpage = newpage;
5188c2ecf20Sopenharmony_ci		newpage = NULL;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	reread_backing_page:
5218c2ecf20Sopenharmony_ci		ret = bmapping->a_ops->readpage(NULL, backpage);
5228c2ecf20Sopenharmony_ci		if (ret < 0)
5238c2ecf20Sopenharmony_ci			goto read_error;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		/* add the netfs page to the pagecache and LRU, and set the
5268c2ecf20Sopenharmony_ci		 * monitor to transfer the data across */
5278c2ecf20Sopenharmony_ci	monitor_backing_page:
5288c2ecf20Sopenharmony_ci		_debug("- monitor add");
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		ret = add_to_page_cache_lru(netpage, op->mapping,
5318c2ecf20Sopenharmony_ci					    netpage->index, cachefiles_gfp);
5328c2ecf20Sopenharmony_ci		if (ret < 0) {
5338c2ecf20Sopenharmony_ci			if (ret == -EEXIST) {
5348c2ecf20Sopenharmony_ci				put_page(backpage);
5358c2ecf20Sopenharmony_ci				backpage = NULL;
5368c2ecf20Sopenharmony_ci				put_page(netpage);
5378c2ecf20Sopenharmony_ci				netpage = NULL;
5388c2ecf20Sopenharmony_ci				fscache_retrieval_complete(op, 1);
5398c2ecf20Sopenharmony_ci				continue;
5408c2ecf20Sopenharmony_ci			}
5418c2ecf20Sopenharmony_ci			goto nomem;
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		/* install a monitor */
5458c2ecf20Sopenharmony_ci		get_page(netpage);
5468c2ecf20Sopenharmony_ci		monitor->netfs_page = netpage;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		get_page(backpage);
5498c2ecf20Sopenharmony_ci		monitor->back_page = backpage;
5508c2ecf20Sopenharmony_ci		monitor->monitor.private = backpage;
5518c2ecf20Sopenharmony_ci		add_page_wait_queue(backpage, &monitor->monitor);
5528c2ecf20Sopenharmony_ci		monitor = NULL;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		/* but the page may have been read before the monitor was
5558c2ecf20Sopenharmony_ci		 * installed, so the monitor may miss the event - so we have to
5568c2ecf20Sopenharmony_ci		 * ensure that we do get one in such a case */
5578c2ecf20Sopenharmony_ci		if (trylock_page(backpage)) {
5588c2ecf20Sopenharmony_ci			_debug("2unlock %p {%lx}", backpage, backpage->flags);
5598c2ecf20Sopenharmony_ci			unlock_page(backpage);
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		put_page(backpage);
5638c2ecf20Sopenharmony_ci		backpage = NULL;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		put_page(netpage);
5668c2ecf20Sopenharmony_ci		netpage = NULL;
5678c2ecf20Sopenharmony_ci		continue;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		/* if the backing page is already present, it can be in one of
5708c2ecf20Sopenharmony_ci		 * three states: read in progress, read failed or read okay */
5718c2ecf20Sopenharmony_ci	backing_page_already_present:
5728c2ecf20Sopenharmony_ci		_debug("- present %p", backpage);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		if (PageError(backpage))
5758c2ecf20Sopenharmony_ci			goto io_error;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		if (PageUptodate(backpage))
5788c2ecf20Sopenharmony_ci			goto backing_page_already_uptodate;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		_debug("- not ready %p{%lx}", backpage, backpage->flags);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		if (!trylock_page(backpage))
5838c2ecf20Sopenharmony_ci			goto monitor_backing_page;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		if (PageError(backpage)) {
5868c2ecf20Sopenharmony_ci			_debug("error %lx", backpage->flags);
5878c2ecf20Sopenharmony_ci			unlock_page(backpage);
5888c2ecf20Sopenharmony_ci			goto io_error;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		if (PageUptodate(backpage))
5928c2ecf20Sopenharmony_ci			goto backing_page_already_uptodate_unlock;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		/* we've locked a page that's neither up to date nor erroneous,
5958c2ecf20Sopenharmony_ci		 * so we need to attempt to read it again */
5968c2ecf20Sopenharmony_ci		goto reread_backing_page;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci		/* the backing page is already up to date, attach the netfs
5998c2ecf20Sopenharmony_ci		 * page to the pagecache and LRU and copy the data across */
6008c2ecf20Sopenharmony_ci	backing_page_already_uptodate_unlock:
6018c2ecf20Sopenharmony_ci		_debug("uptodate %lx", backpage->flags);
6028c2ecf20Sopenharmony_ci		unlock_page(backpage);
6038c2ecf20Sopenharmony_ci	backing_page_already_uptodate:
6048c2ecf20Sopenharmony_ci		_debug("- uptodate");
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		ret = add_to_page_cache_lru(netpage, op->mapping,
6078c2ecf20Sopenharmony_ci					    netpage->index, cachefiles_gfp);
6088c2ecf20Sopenharmony_ci		if (ret < 0) {
6098c2ecf20Sopenharmony_ci			if (ret == -EEXIST) {
6108c2ecf20Sopenharmony_ci				put_page(backpage);
6118c2ecf20Sopenharmony_ci				backpage = NULL;
6128c2ecf20Sopenharmony_ci				put_page(netpage);
6138c2ecf20Sopenharmony_ci				netpage = NULL;
6148c2ecf20Sopenharmony_ci				fscache_retrieval_complete(op, 1);
6158c2ecf20Sopenharmony_ci				continue;
6168c2ecf20Sopenharmony_ci			}
6178c2ecf20Sopenharmony_ci			goto nomem;
6188c2ecf20Sopenharmony_ci		}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci		copy_highpage(netpage, backpage);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		put_page(backpage);
6238c2ecf20Sopenharmony_ci		backpage = NULL;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		fscache_mark_page_cached(op, netpage);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		/* the netpage is unlocked and marked up to date here */
6288c2ecf20Sopenharmony_ci		fscache_end_io(op, netpage, 0);
6298c2ecf20Sopenharmony_ci		put_page(netpage);
6308c2ecf20Sopenharmony_ci		netpage = NULL;
6318c2ecf20Sopenharmony_ci		fscache_retrieval_complete(op, 1);
6328c2ecf20Sopenharmony_ci		continue;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	netpage = NULL;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	_debug("out");
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ciout:
6408c2ecf20Sopenharmony_ci	/* tidy up */
6418c2ecf20Sopenharmony_ci	if (newpage)
6428c2ecf20Sopenharmony_ci		put_page(newpage);
6438c2ecf20Sopenharmony_ci	if (netpage)
6448c2ecf20Sopenharmony_ci		put_page(netpage);
6458c2ecf20Sopenharmony_ci	if (backpage)
6468c2ecf20Sopenharmony_ci		put_page(backpage);
6478c2ecf20Sopenharmony_ci	if (monitor) {
6488c2ecf20Sopenharmony_ci		fscache_put_retrieval(op);
6498c2ecf20Sopenharmony_ci		kfree(monitor);
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	list_for_each_entry_safe(netpage, _n, list, lru) {
6538c2ecf20Sopenharmony_ci		list_del(&netpage->lru);
6548c2ecf20Sopenharmony_ci		put_page(netpage);
6558c2ecf20Sopenharmony_ci		fscache_retrieval_complete(op, 1);
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
6598c2ecf20Sopenharmony_ci	return ret;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cinomem:
6628c2ecf20Sopenharmony_ci	_debug("nomem");
6638c2ecf20Sopenharmony_ci	ret = -ENOMEM;
6648c2ecf20Sopenharmony_ci	goto record_page_complete;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ciread_error:
6678c2ecf20Sopenharmony_ci	_debug("read error %d", ret);
6688c2ecf20Sopenharmony_ci	if (ret == -ENOMEM)
6698c2ecf20Sopenharmony_ci		goto record_page_complete;
6708c2ecf20Sopenharmony_ciio_error:
6718c2ecf20Sopenharmony_ci	cachefiles_io_error_obj(object, "Page read error on backing file");
6728c2ecf20Sopenharmony_ci	ret = -ENOBUFS;
6738c2ecf20Sopenharmony_cirecord_page_complete:
6748c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, 1);
6758c2ecf20Sopenharmony_ci	goto out;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci/*
6798c2ecf20Sopenharmony_ci * read a list of pages from the cache or allocate blocks in which to store
6808c2ecf20Sopenharmony_ci * them
6818c2ecf20Sopenharmony_ci */
6828c2ecf20Sopenharmony_ciint cachefiles_read_or_alloc_pages(struct fscache_retrieval *op,
6838c2ecf20Sopenharmony_ci				   struct list_head *pages,
6848c2ecf20Sopenharmony_ci				   unsigned *nr_pages,
6858c2ecf20Sopenharmony_ci				   gfp_t gfp)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
6888c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
6898c2ecf20Sopenharmony_ci	struct list_head backpages;
6908c2ecf20Sopenharmony_ci	struct pagevec pagevec;
6918c2ecf20Sopenharmony_ci	struct inode *inode;
6928c2ecf20Sopenharmony_ci	struct page *page, *_n;
6938c2ecf20Sopenharmony_ci	unsigned shift, nrbackpages;
6948c2ecf20Sopenharmony_ci	int ret, ret2, space;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	object = container_of(op->op.object,
6978c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
6988c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
6998c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	_enter("{OBJ%x,%d},,%d,,",
7028c2ecf20Sopenharmony_ci	       object->fscache.debug_id, atomic_read(&op->op.usage),
7038c2ecf20Sopenharmony_ci	       *nr_pages);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (!object->backer)
7068c2ecf20Sopenharmony_ci		goto all_enobufs;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	space = 1;
7098c2ecf20Sopenharmony_ci	if (cachefiles_has_space(cache, 0, *nr_pages) < 0)
7108c2ecf20Sopenharmony_ci		space = 0;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	inode = d_backing_inode(object->backer);
7138c2ecf20Sopenharmony_ci	ASSERT(S_ISREG(inode->i_mode));
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/* calculate the shift required to use bmap */
7168c2ecf20Sopenharmony_ci	shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	pagevec_init(&pagevec);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	op->op.flags &= FSCACHE_OP_KEEP_FLAGS;
7218c2ecf20Sopenharmony_ci	op->op.flags |= FSCACHE_OP_ASYNC;
7228c2ecf20Sopenharmony_ci	op->op.processor = cachefiles_read_copier;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&backpages);
7258c2ecf20Sopenharmony_ci	nrbackpages = 0;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	ret = space ? -ENODATA : -ENOBUFS;
7288c2ecf20Sopenharmony_ci	list_for_each_entry_safe(page, _n, pages, lru) {
7298c2ecf20Sopenharmony_ci		sector_t block;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		/* we assume the absence or presence of the first block is a
7328c2ecf20Sopenharmony_ci		 * good enough indication for the page as a whole
7338c2ecf20Sopenharmony_ci		 * - TODO: don't use bmap() for this as it is _not_ actually
7348c2ecf20Sopenharmony_ci		 *   good enough for this as it doesn't indicate errors, but
7358c2ecf20Sopenharmony_ci		 *   it's all we've got for the moment
7368c2ecf20Sopenharmony_ci		 */
7378c2ecf20Sopenharmony_ci		block = page->index;
7388c2ecf20Sopenharmony_ci		block <<= shift;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		ret2 = bmap(inode, &block);
7418c2ecf20Sopenharmony_ci		ASSERT(ret2 == 0);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		_debug("%llx -> %llx",
7448c2ecf20Sopenharmony_ci		       (unsigned long long) (page->index << shift),
7458c2ecf20Sopenharmony_ci		       (unsigned long long) block);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		if (block) {
7488c2ecf20Sopenharmony_ci			/* we have data - add it to the list to give to the
7498c2ecf20Sopenharmony_ci			 * backing fs */
7508c2ecf20Sopenharmony_ci			list_move(&page->lru, &backpages);
7518c2ecf20Sopenharmony_ci			(*nr_pages)--;
7528c2ecf20Sopenharmony_ci			nrbackpages++;
7538c2ecf20Sopenharmony_ci		} else if (space && pagevec_add(&pagevec, page) == 0) {
7548c2ecf20Sopenharmony_ci			fscache_mark_pages_cached(op, &pagevec);
7558c2ecf20Sopenharmony_ci			fscache_retrieval_complete(op, 1);
7568c2ecf20Sopenharmony_ci			ret = -ENODATA;
7578c2ecf20Sopenharmony_ci		} else {
7588c2ecf20Sopenharmony_ci			fscache_retrieval_complete(op, 1);
7598c2ecf20Sopenharmony_ci		}
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	if (pagevec_count(&pagevec) > 0)
7638c2ecf20Sopenharmony_ci		fscache_mark_pages_cached(op, &pagevec);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (list_empty(pages))
7668c2ecf20Sopenharmony_ci		ret = 0;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* submit the apparently valid pages to the backing fs to be read from
7698c2ecf20Sopenharmony_ci	 * disk */
7708c2ecf20Sopenharmony_ci	if (nrbackpages > 0) {
7718c2ecf20Sopenharmony_ci		ret2 = cachefiles_read_backing_file(object, op, &backpages);
7728c2ecf20Sopenharmony_ci		if (ret2 == -ENOMEM || ret2 == -EINTR)
7738c2ecf20Sopenharmony_ci			ret = ret2;
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	_leave(" = %d [nr=%u%s]",
7778c2ecf20Sopenharmony_ci	       ret, *nr_pages, list_empty(pages) ? " empty" : "");
7788c2ecf20Sopenharmony_ci	return ret;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ciall_enobufs:
7818c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, *nr_pages);
7828c2ecf20Sopenharmony_ci	return -ENOBUFS;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci/*
7868c2ecf20Sopenharmony_ci * allocate a block in the cache in which to store a page
7878c2ecf20Sopenharmony_ci * - cache withdrawal is prevented by the caller
7888c2ecf20Sopenharmony_ci * - returns -EINTR if interrupted
7898c2ecf20Sopenharmony_ci * - returns -ENOMEM if ran out of memory
7908c2ecf20Sopenharmony_ci * - returns -ENOBUFS if no buffers can be made available
7918c2ecf20Sopenharmony_ci * - returns -ENOBUFS if page is beyond EOF
7928c2ecf20Sopenharmony_ci * - otherwise:
7938c2ecf20Sopenharmony_ci *   - the metadata will be retained
7948c2ecf20Sopenharmony_ci *   - 0 will be returned
7958c2ecf20Sopenharmony_ci */
7968c2ecf20Sopenharmony_ciint cachefiles_allocate_page(struct fscache_retrieval *op,
7978c2ecf20Sopenharmony_ci			     struct page *page,
7988c2ecf20Sopenharmony_ci			     gfp_t gfp)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
8018c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
8028c2ecf20Sopenharmony_ci	int ret;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	object = container_of(op->op.object,
8058c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
8068c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
8078c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	_enter("%p,{%lx},", object, page->index);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	ret = cachefiles_has_space(cache, 0, 1);
8128c2ecf20Sopenharmony_ci	if (ret == 0)
8138c2ecf20Sopenharmony_ci		fscache_mark_page_cached(op, page);
8148c2ecf20Sopenharmony_ci	else
8158c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, 1);
8188c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
8198c2ecf20Sopenharmony_ci	return ret;
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/*
8238c2ecf20Sopenharmony_ci * allocate blocks in the cache in which to store a set of pages
8248c2ecf20Sopenharmony_ci * - cache withdrawal is prevented by the caller
8258c2ecf20Sopenharmony_ci * - returns -EINTR if interrupted
8268c2ecf20Sopenharmony_ci * - returns -ENOMEM if ran out of memory
8278c2ecf20Sopenharmony_ci * - returns -ENOBUFS if some buffers couldn't be made available
8288c2ecf20Sopenharmony_ci * - returns -ENOBUFS if some pages are beyond EOF
8298c2ecf20Sopenharmony_ci * - otherwise:
8308c2ecf20Sopenharmony_ci *   - -ENODATA will be returned
8318c2ecf20Sopenharmony_ci * - metadata will be retained for any page marked
8328c2ecf20Sopenharmony_ci */
8338c2ecf20Sopenharmony_ciint cachefiles_allocate_pages(struct fscache_retrieval *op,
8348c2ecf20Sopenharmony_ci			      struct list_head *pages,
8358c2ecf20Sopenharmony_ci			      unsigned *nr_pages,
8368c2ecf20Sopenharmony_ci			      gfp_t gfp)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
8398c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
8408c2ecf20Sopenharmony_ci	struct pagevec pagevec;
8418c2ecf20Sopenharmony_ci	struct page *page;
8428c2ecf20Sopenharmony_ci	int ret;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	object = container_of(op->op.object,
8458c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
8468c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
8478c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	_enter("%p,,,%d,", object, *nr_pages);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ret = cachefiles_has_space(cache, 0, *nr_pages);
8528c2ecf20Sopenharmony_ci	if (ret == 0) {
8538c2ecf20Sopenharmony_ci		pagevec_init(&pagevec);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		list_for_each_entry(page, pages, lru) {
8568c2ecf20Sopenharmony_ci			if (pagevec_add(&pagevec, page) == 0)
8578c2ecf20Sopenharmony_ci				fscache_mark_pages_cached(op, &pagevec);
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		if (pagevec_count(&pagevec) > 0)
8618c2ecf20Sopenharmony_ci			fscache_mark_pages_cached(op, &pagevec);
8628c2ecf20Sopenharmony_ci		ret = -ENODATA;
8638c2ecf20Sopenharmony_ci	} else {
8648c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	fscache_retrieval_complete(op, *nr_pages);
8688c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
8698c2ecf20Sopenharmony_ci	return ret;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci/*
8738c2ecf20Sopenharmony_ci * request a page be stored in the cache
8748c2ecf20Sopenharmony_ci * - cache withdrawal is prevented by the caller
8758c2ecf20Sopenharmony_ci * - this request may be ignored if there's no cache block available, in which
8768c2ecf20Sopenharmony_ci *   case -ENOBUFS will be returned
8778c2ecf20Sopenharmony_ci * - if the op is in progress, 0 will be returned
8788c2ecf20Sopenharmony_ci */
8798c2ecf20Sopenharmony_ciint cachefiles_write_page(struct fscache_storage *op, struct page *page)
8808c2ecf20Sopenharmony_ci{
8818c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
8828c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
8838c2ecf20Sopenharmony_ci	struct file *file;
8848c2ecf20Sopenharmony_ci	struct path path;
8858c2ecf20Sopenharmony_ci	loff_t pos, eof;
8868c2ecf20Sopenharmony_ci	size_t len;
8878c2ecf20Sopenharmony_ci	void *data;
8888c2ecf20Sopenharmony_ci	int ret = -ENOBUFS;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	ASSERT(op != NULL);
8918c2ecf20Sopenharmony_ci	ASSERT(page != NULL);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	object = container_of(op->op.object,
8948c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	_enter("%p,%p{%lx},,,", object, page, page->index);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (!object->backer) {
8998c2ecf20Sopenharmony_ci		_leave(" = -ENOBUFS");
9008c2ecf20Sopenharmony_ci		return -ENOBUFS;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	ASSERT(d_is_reg(object->backer));
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
9068c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	pos = (loff_t)page->index << PAGE_SHIFT;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	/* We mustn't write more data than we have, so we have to beware of a
9118c2ecf20Sopenharmony_ci	 * partial page at EOF.
9128c2ecf20Sopenharmony_ci	 */
9138c2ecf20Sopenharmony_ci	eof = object->fscache.store_limit_l;
9148c2ecf20Sopenharmony_ci	if (pos >= eof)
9158c2ecf20Sopenharmony_ci		goto error;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* write the page to the backing filesystem and let it store it in its
9188c2ecf20Sopenharmony_ci	 * own time */
9198c2ecf20Sopenharmony_ci	path.mnt = cache->mnt;
9208c2ecf20Sopenharmony_ci	path.dentry = object->backer;
9218c2ecf20Sopenharmony_ci	file = dentry_open(&path, O_RDWR | O_LARGEFILE, cache->cache_cred);
9228c2ecf20Sopenharmony_ci	if (IS_ERR(file)) {
9238c2ecf20Sopenharmony_ci		ret = PTR_ERR(file);
9248c2ecf20Sopenharmony_ci		goto error_2;
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	len = PAGE_SIZE;
9288c2ecf20Sopenharmony_ci	if (eof & ~PAGE_MASK) {
9298c2ecf20Sopenharmony_ci		if (eof - pos < PAGE_SIZE) {
9308c2ecf20Sopenharmony_ci			_debug("cut short %llx to %llx",
9318c2ecf20Sopenharmony_ci			       pos, eof);
9328c2ecf20Sopenharmony_ci			len = eof - pos;
9338c2ecf20Sopenharmony_ci			ASSERTCMP(pos + len, ==, eof);
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	data = kmap(page);
9388c2ecf20Sopenharmony_ci	ret = kernel_write(file, data, len, &pos);
9398c2ecf20Sopenharmony_ci	kunmap(page);
9408c2ecf20Sopenharmony_ci	fput(file);
9418c2ecf20Sopenharmony_ci	if (ret != len)
9428c2ecf20Sopenharmony_ci		goto error_eio;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	_leave(" = 0");
9458c2ecf20Sopenharmony_ci	return 0;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cierror_eio:
9488c2ecf20Sopenharmony_ci	ret = -EIO;
9498c2ecf20Sopenharmony_cierror_2:
9508c2ecf20Sopenharmony_ci	if (ret == -EIO)
9518c2ecf20Sopenharmony_ci		cachefiles_io_error_obj(object,
9528c2ecf20Sopenharmony_ci					"Write page to backing file failed");
9538c2ecf20Sopenharmony_cierror:
9548c2ecf20Sopenharmony_ci	_leave(" = -ENOBUFS [%d]", ret);
9558c2ecf20Sopenharmony_ci	return -ENOBUFS;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci/*
9598c2ecf20Sopenharmony_ci * detach a backing block from a page
9608c2ecf20Sopenharmony_ci * - cache withdrawal is prevented by the caller
9618c2ecf20Sopenharmony_ci */
9628c2ecf20Sopenharmony_civoid cachefiles_uncache_page(struct fscache_object *_object, struct page *page)
9638c2ecf20Sopenharmony_ci	__releases(&object->fscache.cookie->lock)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	_enter("%p,{%lu}", object, page->index);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	spin_unlock(&object->fscache.cookie->lock);
9728c2ecf20Sopenharmony_ci}
973