xref: /kernel/linux/linux-5.10/fs/afs/write.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* handling of writes to regular files and writing back to the server
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/backing-dev.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/fs.h>
118c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
128c2ecf20Sopenharmony_ci#include <linux/writeback.h>
138c2ecf20Sopenharmony_ci#include <linux/pagevec.h>
148c2ecf20Sopenharmony_ci#include "internal.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * mark a page as having been made dirty and thus needing writeback
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ciint afs_set_page_dirty(struct page *page)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	_enter("");
228c2ecf20Sopenharmony_ci	return __set_page_dirty_nobuffers(page);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * partly or wholly fill a page that's under preparation for writing
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cistatic int afs_fill_page(struct afs_vnode *vnode, struct key *key,
298c2ecf20Sopenharmony_ci			 loff_t pos, unsigned int len, struct page *page)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct afs_read *req;
328c2ecf20Sopenharmony_ci	size_t p;
338c2ecf20Sopenharmony_ci	void *data;
348c2ecf20Sopenharmony_ci	int ret;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	_enter(",,%llu", (unsigned long long)pos);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (pos >= vnode->vfs_inode.i_size) {
398c2ecf20Sopenharmony_ci		p = pos & ~PAGE_MASK;
408c2ecf20Sopenharmony_ci		ASSERTCMP(p + len, <=, PAGE_SIZE);
418c2ecf20Sopenharmony_ci		data = kmap(page);
428c2ecf20Sopenharmony_ci		memset(data + p, 0, len);
438c2ecf20Sopenharmony_ci		kunmap(page);
448c2ecf20Sopenharmony_ci		return 0;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	req = kzalloc(struct_size(req, array, 1), GFP_KERNEL);
488c2ecf20Sopenharmony_ci	if (!req)
498c2ecf20Sopenharmony_ci		return -ENOMEM;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	refcount_set(&req->usage, 1);
528c2ecf20Sopenharmony_ci	req->pos = pos;
538c2ecf20Sopenharmony_ci	req->len = len;
548c2ecf20Sopenharmony_ci	req->nr_pages = 1;
558c2ecf20Sopenharmony_ci	req->pages = req->array;
568c2ecf20Sopenharmony_ci	req->pages[0] = page;
578c2ecf20Sopenharmony_ci	get_page(page);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ret = afs_fetch_data(vnode, key, req);
608c2ecf20Sopenharmony_ci	afs_put_read(req);
618c2ecf20Sopenharmony_ci	if (ret < 0) {
628c2ecf20Sopenharmony_ci		if (ret == -ENOENT) {
638c2ecf20Sopenharmony_ci			_debug("got NOENT from server"
648c2ecf20Sopenharmony_ci			       " - marking file deleted and stale");
658c2ecf20Sopenharmony_ci			set_bit(AFS_VNODE_DELETED, &vnode->flags);
668c2ecf20Sopenharmony_ci			ret = -ESTALE;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
718c2ecf20Sopenharmony_ci	return ret;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * prepare to perform part of a write to a page
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_ciint afs_write_begin(struct file *file, struct address_space *mapping,
788c2ecf20Sopenharmony_ci		    loff_t pos, unsigned len, unsigned flags,
798c2ecf20Sopenharmony_ci		    struct page **_page, void **fsdata)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
828c2ecf20Sopenharmony_ci	struct page *page;
838c2ecf20Sopenharmony_ci	struct key *key = afs_file_key(file);
848c2ecf20Sopenharmony_ci	unsigned long priv;
858c2ecf20Sopenharmony_ci	unsigned f, from = pos & (PAGE_SIZE - 1);
868c2ecf20Sopenharmony_ci	unsigned t, to = from + len;
878c2ecf20Sopenharmony_ci	pgoff_t index = pos >> PAGE_SHIFT;
888c2ecf20Sopenharmony_ci	int ret;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},{%lx},%u,%u",
918c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, index, from, to);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	page = grab_cache_page_write_begin(mapping, index, flags);
948c2ecf20Sopenharmony_ci	if (!page)
958c2ecf20Sopenharmony_ci		return -ENOMEM;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (!PageUptodate(page) && len != PAGE_SIZE) {
988c2ecf20Sopenharmony_ci		ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page);
998c2ecf20Sopenharmony_ci		if (ret < 0) {
1008c2ecf20Sopenharmony_ci			unlock_page(page);
1018c2ecf20Sopenharmony_ci			put_page(page);
1028c2ecf20Sopenharmony_ci			_leave(" = %d [prep]", ret);
1038c2ecf20Sopenharmony_ci			return ret;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci		SetPageUptodate(page);
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_citry_again:
1098c2ecf20Sopenharmony_ci	/* See if this page is already partially written in a way that we can
1108c2ecf20Sopenharmony_ci	 * merge the new write with.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	t = f = 0;
1138c2ecf20Sopenharmony_ci	if (PagePrivate(page)) {
1148c2ecf20Sopenharmony_ci		priv = page_private(page);
1158c2ecf20Sopenharmony_ci		f = afs_page_dirty_from(priv);
1168c2ecf20Sopenharmony_ci		t = afs_page_dirty_to(priv);
1178c2ecf20Sopenharmony_ci		ASSERTCMP(f, <=, t);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (f != t) {
1218c2ecf20Sopenharmony_ci		if (PageWriteback(page)) {
1228c2ecf20Sopenharmony_ci			trace_afs_page_dirty(vnode, tracepoint_string("alrdy"),
1238c2ecf20Sopenharmony_ci					     page->index, priv);
1248c2ecf20Sopenharmony_ci			goto flush_conflicting_write;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		/* If the file is being filled locally, allow inter-write
1278c2ecf20Sopenharmony_ci		 * spaces to be merged into writes.  If it's not, only write
1288c2ecf20Sopenharmony_ci		 * back what the user gives us.
1298c2ecf20Sopenharmony_ci		 */
1308c2ecf20Sopenharmony_ci		if (!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags) &&
1318c2ecf20Sopenharmony_ci		    (to < f || from > t))
1328c2ecf20Sopenharmony_ci			goto flush_conflicting_write;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	*_page = page;
1368c2ecf20Sopenharmony_ci	_leave(" = 0");
1378c2ecf20Sopenharmony_ci	return 0;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* The previous write and this write aren't adjacent or overlapping, so
1408c2ecf20Sopenharmony_ci	 * flush the page out.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ciflush_conflicting_write:
1438c2ecf20Sopenharmony_ci	_debug("flush conflict");
1448c2ecf20Sopenharmony_ci	ret = write_one_page(page);
1458c2ecf20Sopenharmony_ci	if (ret < 0)
1468c2ecf20Sopenharmony_ci		goto error;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ret = lock_page_killable(page);
1498c2ecf20Sopenharmony_ci	if (ret < 0)
1508c2ecf20Sopenharmony_ci		goto error;
1518c2ecf20Sopenharmony_ci	goto try_again;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cierror:
1548c2ecf20Sopenharmony_ci	put_page(page);
1558c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
1568c2ecf20Sopenharmony_ci	return ret;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/*
1608c2ecf20Sopenharmony_ci * finalise part of a write to a page
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_ciint afs_write_end(struct file *file, struct address_space *mapping,
1638c2ecf20Sopenharmony_ci		  loff_t pos, unsigned len, unsigned copied,
1648c2ecf20Sopenharmony_ci		  struct page *page, void *fsdata)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
1678c2ecf20Sopenharmony_ci	struct key *key = afs_file_key(file);
1688c2ecf20Sopenharmony_ci	unsigned long priv;
1698c2ecf20Sopenharmony_ci	unsigned int f, from = pos & (PAGE_SIZE - 1);
1708c2ecf20Sopenharmony_ci	unsigned int t, to = from + copied;
1718c2ecf20Sopenharmony_ci	loff_t i_size, maybe_i_size;
1728c2ecf20Sopenharmony_ci	int ret = 0;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},{%lx}",
1758c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, page->index);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (copied == 0)
1788c2ecf20Sopenharmony_ci		goto out;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	maybe_i_size = pos + copied;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	i_size = i_size_read(&vnode->vfs_inode);
1838c2ecf20Sopenharmony_ci	if (maybe_i_size > i_size) {
1848c2ecf20Sopenharmony_ci		write_seqlock(&vnode->cb_lock);
1858c2ecf20Sopenharmony_ci		i_size = i_size_read(&vnode->vfs_inode);
1868c2ecf20Sopenharmony_ci		if (maybe_i_size > i_size)
1878c2ecf20Sopenharmony_ci			afs_set_i_size(vnode, maybe_i_size);
1888c2ecf20Sopenharmony_ci		write_sequnlock(&vnode->cb_lock);
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (!PageUptodate(page)) {
1928c2ecf20Sopenharmony_ci		if (copied < len) {
1938c2ecf20Sopenharmony_ci			/* Try and load any missing data from the server.  The
1948c2ecf20Sopenharmony_ci			 * unmarshalling routine will take care of clearing any
1958c2ecf20Sopenharmony_ci			 * bits that are beyond the EOF.
1968c2ecf20Sopenharmony_ci			 */
1978c2ecf20Sopenharmony_ci			ret = afs_fill_page(vnode, key, pos + copied,
1988c2ecf20Sopenharmony_ci					    len - copied, page);
1998c2ecf20Sopenharmony_ci			if (ret < 0)
2008c2ecf20Sopenharmony_ci				goto out;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci		SetPageUptodate(page);
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (PagePrivate(page)) {
2068c2ecf20Sopenharmony_ci		priv = page_private(page);
2078c2ecf20Sopenharmony_ci		f = afs_page_dirty_from(priv);
2088c2ecf20Sopenharmony_ci		t = afs_page_dirty_to(priv);
2098c2ecf20Sopenharmony_ci		if (from < f)
2108c2ecf20Sopenharmony_ci			f = from;
2118c2ecf20Sopenharmony_ci		if (to > t)
2128c2ecf20Sopenharmony_ci			t = to;
2138c2ecf20Sopenharmony_ci		priv = afs_page_dirty(f, t);
2148c2ecf20Sopenharmony_ci		set_page_private(page, priv);
2158c2ecf20Sopenharmony_ci		trace_afs_page_dirty(vnode, tracepoint_string("dirty+"),
2168c2ecf20Sopenharmony_ci				     page->index, priv);
2178c2ecf20Sopenharmony_ci	} else {
2188c2ecf20Sopenharmony_ci		priv = afs_page_dirty(from, to);
2198c2ecf20Sopenharmony_ci		attach_page_private(page, (void *)priv);
2208c2ecf20Sopenharmony_ci		trace_afs_page_dirty(vnode, tracepoint_string("dirty"),
2218c2ecf20Sopenharmony_ci				     page->index, priv);
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	set_page_dirty(page);
2258c2ecf20Sopenharmony_ci	if (PageDirty(page))
2268c2ecf20Sopenharmony_ci		_debug("dirtied");
2278c2ecf20Sopenharmony_ci	ret = copied;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciout:
2308c2ecf20Sopenharmony_ci	unlock_page(page);
2318c2ecf20Sopenharmony_ci	put_page(page);
2328c2ecf20Sopenharmony_ci	return ret;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/*
2368c2ecf20Sopenharmony_ci * kill all the pages in the given range
2378c2ecf20Sopenharmony_ci */
2388c2ecf20Sopenharmony_cistatic void afs_kill_pages(struct address_space *mapping,
2398c2ecf20Sopenharmony_ci			   pgoff_t first, pgoff_t last)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
2428c2ecf20Sopenharmony_ci	struct pagevec pv;
2438c2ecf20Sopenharmony_ci	unsigned count, loop;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},%lx-%lx",
2468c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, first, last);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	pagevec_init(&pv);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	do {
2518c2ecf20Sopenharmony_ci		_debug("kill %lx-%lx", first, last);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		count = last - first + 1;
2548c2ecf20Sopenharmony_ci		if (count > PAGEVEC_SIZE)
2558c2ecf20Sopenharmony_ci			count = PAGEVEC_SIZE;
2568c2ecf20Sopenharmony_ci		pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
2578c2ecf20Sopenharmony_ci		ASSERTCMP(pv.nr, ==, count);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		for (loop = 0; loop < count; loop++) {
2608c2ecf20Sopenharmony_ci			struct page *page = pv.pages[loop];
2618c2ecf20Sopenharmony_ci			ClearPageUptodate(page);
2628c2ecf20Sopenharmony_ci			SetPageError(page);
2638c2ecf20Sopenharmony_ci			end_page_writeback(page);
2648c2ecf20Sopenharmony_ci			if (page->index >= first)
2658c2ecf20Sopenharmony_ci				first = page->index + 1;
2668c2ecf20Sopenharmony_ci			lock_page(page);
2678c2ecf20Sopenharmony_ci			generic_error_remove_page(mapping, page);
2688c2ecf20Sopenharmony_ci			unlock_page(page);
2698c2ecf20Sopenharmony_ci		}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		__pagevec_release(&pv);
2728c2ecf20Sopenharmony_ci	} while (first <= last);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	_leave("");
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/*
2788c2ecf20Sopenharmony_ci * Redirty all the pages in a given range.
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_cistatic void afs_redirty_pages(struct writeback_control *wbc,
2818c2ecf20Sopenharmony_ci			      struct address_space *mapping,
2828c2ecf20Sopenharmony_ci			      pgoff_t first, pgoff_t last)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
2858c2ecf20Sopenharmony_ci	struct pagevec pv;
2868c2ecf20Sopenharmony_ci	unsigned count, loop;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},%lx-%lx",
2898c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, first, last);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	pagevec_init(&pv);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	do {
2948c2ecf20Sopenharmony_ci		_debug("redirty %lx-%lx", first, last);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		count = last - first + 1;
2978c2ecf20Sopenharmony_ci		if (count > PAGEVEC_SIZE)
2988c2ecf20Sopenharmony_ci			count = PAGEVEC_SIZE;
2998c2ecf20Sopenharmony_ci		pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
3008c2ecf20Sopenharmony_ci		ASSERTCMP(pv.nr, ==, count);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		for (loop = 0; loop < count; loop++) {
3038c2ecf20Sopenharmony_ci			struct page *page = pv.pages[loop];
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci			redirty_page_for_writepage(wbc, page);
3068c2ecf20Sopenharmony_ci			end_page_writeback(page);
3078c2ecf20Sopenharmony_ci			if (page->index >= first)
3088c2ecf20Sopenharmony_ci				first = page->index + 1;
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		__pagevec_release(&pv);
3128c2ecf20Sopenharmony_ci	} while (first <= last);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	_leave("");
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/*
3188c2ecf20Sopenharmony_ci * completion of write to server
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_cistatic void afs_pages_written_back(struct afs_vnode *vnode,
3218c2ecf20Sopenharmony_ci				   pgoff_t first, pgoff_t last)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct pagevec pv;
3248c2ecf20Sopenharmony_ci	unsigned long priv;
3258c2ecf20Sopenharmony_ci	unsigned count, loop;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},{%lx-%lx}",
3288c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, first, last);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	pagevec_init(&pv);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	do {
3338c2ecf20Sopenharmony_ci		_debug("done %lx-%lx", first, last);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		count = last - first + 1;
3368c2ecf20Sopenharmony_ci		if (count > PAGEVEC_SIZE)
3378c2ecf20Sopenharmony_ci			count = PAGEVEC_SIZE;
3388c2ecf20Sopenharmony_ci		pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,
3398c2ecf20Sopenharmony_ci					      first, count, pv.pages);
3408c2ecf20Sopenharmony_ci		ASSERTCMP(pv.nr, ==, count);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		for (loop = 0; loop < count; loop++) {
3438c2ecf20Sopenharmony_ci			priv = (unsigned long)detach_page_private(pv.pages[loop]);
3448c2ecf20Sopenharmony_ci			trace_afs_page_dirty(vnode, tracepoint_string("clear"),
3458c2ecf20Sopenharmony_ci					     pv.pages[loop]->index, priv);
3468c2ecf20Sopenharmony_ci			end_page_writeback(pv.pages[loop]);
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci		first += count;
3498c2ecf20Sopenharmony_ci		__pagevec_release(&pv);
3508c2ecf20Sopenharmony_ci	} while (first <= last);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	afs_prune_wb_keys(vnode);
3538c2ecf20Sopenharmony_ci	_leave("");
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/*
3578c2ecf20Sopenharmony_ci * Find a key to use for the writeback.  We cached the keys used to author the
3588c2ecf20Sopenharmony_ci * writes on the vnode.  *_wbk will contain the last writeback key used or NULL
3598c2ecf20Sopenharmony_ci * and we need to start from there if it's set.
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_cistatic int afs_get_writeback_key(struct afs_vnode *vnode,
3628c2ecf20Sopenharmony_ci				 struct afs_wb_key **_wbk)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct afs_wb_key *wbk = NULL;
3658c2ecf20Sopenharmony_ci	struct list_head *p;
3668c2ecf20Sopenharmony_ci	int ret = -ENOKEY, ret2;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	spin_lock(&vnode->wb_lock);
3698c2ecf20Sopenharmony_ci	if (*_wbk)
3708c2ecf20Sopenharmony_ci		p = (*_wbk)->vnode_link.next;
3718c2ecf20Sopenharmony_ci	else
3728c2ecf20Sopenharmony_ci		p = vnode->wb_keys.next;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	while (p != &vnode->wb_keys) {
3758c2ecf20Sopenharmony_ci		wbk = list_entry(p, struct afs_wb_key, vnode_link);
3768c2ecf20Sopenharmony_ci		_debug("wbk %u", key_serial(wbk->key));
3778c2ecf20Sopenharmony_ci		ret2 = key_validate(wbk->key);
3788c2ecf20Sopenharmony_ci		if (ret2 == 0) {
3798c2ecf20Sopenharmony_ci			refcount_inc(&wbk->usage);
3808c2ecf20Sopenharmony_ci			_debug("USE WB KEY %u", key_serial(wbk->key));
3818c2ecf20Sopenharmony_ci			break;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		wbk = NULL;
3858c2ecf20Sopenharmony_ci		if (ret == -ENOKEY)
3868c2ecf20Sopenharmony_ci			ret = ret2;
3878c2ecf20Sopenharmony_ci		p = p->next;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	spin_unlock(&vnode->wb_lock);
3918c2ecf20Sopenharmony_ci	if (*_wbk)
3928c2ecf20Sopenharmony_ci		afs_put_wb_key(*_wbk);
3938c2ecf20Sopenharmony_ci	*_wbk = wbk;
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic void afs_store_data_success(struct afs_operation *op)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = op->file[0].vnode;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	op->ctime = op->file[0].scb.status.mtime_client;
4028c2ecf20Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[0]);
4038c2ecf20Sopenharmony_ci	if (op->error == 0) {
4048c2ecf20Sopenharmony_ci		if (!op->store.laundering)
4058c2ecf20Sopenharmony_ci			afs_pages_written_back(vnode, op->store.first, op->store.last);
4068c2ecf20Sopenharmony_ci		afs_stat_v(vnode, n_stores);
4078c2ecf20Sopenharmony_ci		atomic_long_add((op->store.last * PAGE_SIZE + op->store.last_to) -
4088c2ecf20Sopenharmony_ci				(op->store.first * PAGE_SIZE + op->store.first_offset),
4098c2ecf20Sopenharmony_ci				&afs_v2net(vnode)->n_store_bytes);
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic const struct afs_operation_ops afs_store_data_operation = {
4148c2ecf20Sopenharmony_ci	.issue_afs_rpc	= afs_fs_store_data,
4158c2ecf20Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_store_data,
4168c2ecf20Sopenharmony_ci	.success	= afs_store_data_success,
4178c2ecf20Sopenharmony_ci};
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci/*
4208c2ecf20Sopenharmony_ci * write to a file
4218c2ecf20Sopenharmony_ci */
4228c2ecf20Sopenharmony_cistatic int afs_store_data(struct address_space *mapping,
4238c2ecf20Sopenharmony_ci			  pgoff_t first, pgoff_t last,
4248c2ecf20Sopenharmony_ci			  unsigned offset, unsigned to, bool laundering)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
4278c2ecf20Sopenharmony_ci	struct afs_operation *op;
4288c2ecf20Sopenharmony_ci	struct afs_wb_key *wbk = NULL;
4298c2ecf20Sopenharmony_ci	int ret;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	_enter("%s{%llx:%llu.%u},%lx,%lx,%x,%x",
4328c2ecf20Sopenharmony_ci	       vnode->volume->name,
4338c2ecf20Sopenharmony_ci	       vnode->fid.vid,
4348c2ecf20Sopenharmony_ci	       vnode->fid.vnode,
4358c2ecf20Sopenharmony_ci	       vnode->fid.unique,
4368c2ecf20Sopenharmony_ci	       first, last, offset, to);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ret = afs_get_writeback_key(vnode, &wbk);
4398c2ecf20Sopenharmony_ci	if (ret) {
4408c2ecf20Sopenharmony_ci		_leave(" = %d [no keys]", ret);
4418c2ecf20Sopenharmony_ci		return ret;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	op = afs_alloc_operation(wbk->key, vnode->volume);
4458c2ecf20Sopenharmony_ci	if (IS_ERR(op)) {
4468c2ecf20Sopenharmony_ci		afs_put_wb_key(wbk);
4478c2ecf20Sopenharmony_ci		return -ENOMEM;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	afs_op_set_vnode(op, 0, vnode);
4518c2ecf20Sopenharmony_ci	op->file[0].dv_delta = 1;
4528c2ecf20Sopenharmony_ci	op->store.mapping = mapping;
4538c2ecf20Sopenharmony_ci	op->file[0].modification = true;
4548c2ecf20Sopenharmony_ci	op->store.first = first;
4558c2ecf20Sopenharmony_ci	op->store.last = last;
4568c2ecf20Sopenharmony_ci	op->store.first_offset = offset;
4578c2ecf20Sopenharmony_ci	op->store.last_to = to;
4588c2ecf20Sopenharmony_ci	op->store.laundering = laundering;
4598c2ecf20Sopenharmony_ci	op->mtime = vnode->vfs_inode.i_mtime;
4608c2ecf20Sopenharmony_ci	op->flags |= AFS_OPERATION_UNINTR;
4618c2ecf20Sopenharmony_ci	op->ops = &afs_store_data_operation;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_citry_next_key:
4648c2ecf20Sopenharmony_ci	afs_begin_vnode_operation(op);
4658c2ecf20Sopenharmony_ci	afs_wait_for_operation(op);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	switch (op->error) {
4688c2ecf20Sopenharmony_ci	case -EACCES:
4698c2ecf20Sopenharmony_ci	case -EPERM:
4708c2ecf20Sopenharmony_ci	case -ENOKEY:
4718c2ecf20Sopenharmony_ci	case -EKEYEXPIRED:
4728c2ecf20Sopenharmony_ci	case -EKEYREJECTED:
4738c2ecf20Sopenharmony_ci	case -EKEYREVOKED:
4748c2ecf20Sopenharmony_ci		_debug("next");
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		ret = afs_get_writeback_key(vnode, &wbk);
4778c2ecf20Sopenharmony_ci		if (ret == 0) {
4788c2ecf20Sopenharmony_ci			key_put(op->key);
4798c2ecf20Sopenharmony_ci			op->key = key_get(wbk->key);
4808c2ecf20Sopenharmony_ci			goto try_next_key;
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci		break;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	afs_put_wb_key(wbk);
4868c2ecf20Sopenharmony_ci	_leave(" = %d", op->error);
4878c2ecf20Sopenharmony_ci	return afs_put_operation(op);
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/*
4918c2ecf20Sopenharmony_ci * Synchronously write back the locked page and any subsequent non-locked dirty
4928c2ecf20Sopenharmony_ci * pages.
4938c2ecf20Sopenharmony_ci */
4948c2ecf20Sopenharmony_cistatic int afs_write_back_from_locked_page(struct address_space *mapping,
4958c2ecf20Sopenharmony_ci					   struct writeback_control *wbc,
4968c2ecf20Sopenharmony_ci					   struct page *primary_page,
4978c2ecf20Sopenharmony_ci					   pgoff_t final_page)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
5008c2ecf20Sopenharmony_ci	struct page *pages[8], *page;
5018c2ecf20Sopenharmony_ci	unsigned long count, priv;
5028c2ecf20Sopenharmony_ci	unsigned n, offset, to, f, t;
5038c2ecf20Sopenharmony_ci	pgoff_t start, first, last;
5048c2ecf20Sopenharmony_ci	loff_t i_size, end;
5058c2ecf20Sopenharmony_ci	int loop, ret;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	_enter(",%lx", primary_page->index);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	count = 1;
5108c2ecf20Sopenharmony_ci	if (test_set_page_writeback(primary_page))
5118c2ecf20Sopenharmony_ci		BUG();
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* Find all consecutive lockable dirty pages that have contiguous
5148c2ecf20Sopenharmony_ci	 * written regions, stopping when we find a page that is not
5158c2ecf20Sopenharmony_ci	 * immediately lockable, is not dirty or is missing, or we reach the
5168c2ecf20Sopenharmony_ci	 * end of the range.
5178c2ecf20Sopenharmony_ci	 */
5188c2ecf20Sopenharmony_ci	start = primary_page->index;
5198c2ecf20Sopenharmony_ci	priv = page_private(primary_page);
5208c2ecf20Sopenharmony_ci	offset = afs_page_dirty_from(priv);
5218c2ecf20Sopenharmony_ci	to = afs_page_dirty_to(priv);
5228c2ecf20Sopenharmony_ci	trace_afs_page_dirty(vnode, tracepoint_string("store"),
5238c2ecf20Sopenharmony_ci			     primary_page->index, priv);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	WARN_ON(offset == to);
5268c2ecf20Sopenharmony_ci	if (offset == to)
5278c2ecf20Sopenharmony_ci		trace_afs_page_dirty(vnode, tracepoint_string("WARN"),
5288c2ecf20Sopenharmony_ci				     primary_page->index, priv);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (start >= final_page ||
5318c2ecf20Sopenharmony_ci	    (to < PAGE_SIZE && !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)))
5328c2ecf20Sopenharmony_ci		goto no_more;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	start++;
5358c2ecf20Sopenharmony_ci	do {
5368c2ecf20Sopenharmony_ci		_debug("more %lx [%lx]", start, count);
5378c2ecf20Sopenharmony_ci		n = final_page - start + 1;
5388c2ecf20Sopenharmony_ci		if (n > ARRAY_SIZE(pages))
5398c2ecf20Sopenharmony_ci			n = ARRAY_SIZE(pages);
5408c2ecf20Sopenharmony_ci		n = find_get_pages_contig(mapping, start, ARRAY_SIZE(pages), pages);
5418c2ecf20Sopenharmony_ci		_debug("fgpc %u", n);
5428c2ecf20Sopenharmony_ci		if (n == 0)
5438c2ecf20Sopenharmony_ci			goto no_more;
5448c2ecf20Sopenharmony_ci		if (pages[0]->index != start) {
5458c2ecf20Sopenharmony_ci			do {
5468c2ecf20Sopenharmony_ci				put_page(pages[--n]);
5478c2ecf20Sopenharmony_ci			} while (n > 0);
5488c2ecf20Sopenharmony_ci			goto no_more;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		for (loop = 0; loop < n; loop++) {
5528c2ecf20Sopenharmony_ci			page = pages[loop];
5538c2ecf20Sopenharmony_ci			if (to != PAGE_SIZE &&
5548c2ecf20Sopenharmony_ci			    !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags))
5558c2ecf20Sopenharmony_ci				break;
5568c2ecf20Sopenharmony_ci			if (page->index > final_page)
5578c2ecf20Sopenharmony_ci				break;
5588c2ecf20Sopenharmony_ci			if (!trylock_page(page))
5598c2ecf20Sopenharmony_ci				break;
5608c2ecf20Sopenharmony_ci			if (!PageDirty(page) || PageWriteback(page)) {
5618c2ecf20Sopenharmony_ci				unlock_page(page);
5628c2ecf20Sopenharmony_ci				break;
5638c2ecf20Sopenharmony_ci			}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci			priv = page_private(page);
5668c2ecf20Sopenharmony_ci			f = afs_page_dirty_from(priv);
5678c2ecf20Sopenharmony_ci			t = afs_page_dirty_to(priv);
5688c2ecf20Sopenharmony_ci			if (f != 0 &&
5698c2ecf20Sopenharmony_ci			    !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) {
5708c2ecf20Sopenharmony_ci				unlock_page(page);
5718c2ecf20Sopenharmony_ci				break;
5728c2ecf20Sopenharmony_ci			}
5738c2ecf20Sopenharmony_ci			to = t;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci			trace_afs_page_dirty(vnode, tracepoint_string("store+"),
5768c2ecf20Sopenharmony_ci					     page->index, priv);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci			if (!clear_page_dirty_for_io(page))
5798c2ecf20Sopenharmony_ci				BUG();
5808c2ecf20Sopenharmony_ci			if (test_set_page_writeback(page))
5818c2ecf20Sopenharmony_ci				BUG();
5828c2ecf20Sopenharmony_ci			unlock_page(page);
5838c2ecf20Sopenharmony_ci			put_page(page);
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci		count += loop;
5868c2ecf20Sopenharmony_ci		if (loop < n) {
5878c2ecf20Sopenharmony_ci			for (; loop < n; loop++)
5888c2ecf20Sopenharmony_ci				put_page(pages[loop]);
5898c2ecf20Sopenharmony_ci			goto no_more;
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		start += loop;
5938c2ecf20Sopenharmony_ci	} while (start <= final_page && count < 65536);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cino_more:
5968c2ecf20Sopenharmony_ci	/* We now have a contiguous set of dirty pages, each with writeback
5978c2ecf20Sopenharmony_ci	 * set; the first page is still locked at this point, but all the rest
5988c2ecf20Sopenharmony_ci	 * have been unlocked.
5998c2ecf20Sopenharmony_ci	 */
6008c2ecf20Sopenharmony_ci	unlock_page(primary_page);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	first = primary_page->index;
6038c2ecf20Sopenharmony_ci	last = first + count - 1;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	end = (loff_t)last * PAGE_SIZE + to;
6068c2ecf20Sopenharmony_ci	i_size = i_size_read(&vnode->vfs_inode);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	_debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to);
6098c2ecf20Sopenharmony_ci	if (end > i_size)
6108c2ecf20Sopenharmony_ci		to = i_size & ~PAGE_MASK;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	ret = afs_store_data(mapping, first, last, offset, to, false);
6138c2ecf20Sopenharmony_ci	switch (ret) {
6148c2ecf20Sopenharmony_ci	case 0:
6158c2ecf20Sopenharmony_ci		ret = count;
6168c2ecf20Sopenharmony_ci		break;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	default:
6198c2ecf20Sopenharmony_ci		pr_notice("kAFS: Unexpected error from FS.StoreData %d\n", ret);
6208c2ecf20Sopenharmony_ci		fallthrough;
6218c2ecf20Sopenharmony_ci	case -EACCES:
6228c2ecf20Sopenharmony_ci	case -EPERM:
6238c2ecf20Sopenharmony_ci	case -ENOKEY:
6248c2ecf20Sopenharmony_ci	case -EKEYEXPIRED:
6258c2ecf20Sopenharmony_ci	case -EKEYREJECTED:
6268c2ecf20Sopenharmony_ci	case -EKEYREVOKED:
6278c2ecf20Sopenharmony_ci		afs_redirty_pages(wbc, mapping, first, last);
6288c2ecf20Sopenharmony_ci		mapping_set_error(mapping, ret);
6298c2ecf20Sopenharmony_ci		break;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	case -EDQUOT:
6328c2ecf20Sopenharmony_ci	case -ENOSPC:
6338c2ecf20Sopenharmony_ci		afs_redirty_pages(wbc, mapping, first, last);
6348c2ecf20Sopenharmony_ci		mapping_set_error(mapping, -ENOSPC);
6358c2ecf20Sopenharmony_ci		break;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	case -EROFS:
6388c2ecf20Sopenharmony_ci	case -EIO:
6398c2ecf20Sopenharmony_ci	case -EREMOTEIO:
6408c2ecf20Sopenharmony_ci	case -EFBIG:
6418c2ecf20Sopenharmony_ci	case -ENOENT:
6428c2ecf20Sopenharmony_ci	case -ENOMEDIUM:
6438c2ecf20Sopenharmony_ci	case -ENXIO:
6448c2ecf20Sopenharmony_ci		trace_afs_file_error(vnode, ret, afs_file_error_writeback_fail);
6458c2ecf20Sopenharmony_ci		afs_kill_pages(mapping, first, last);
6468c2ecf20Sopenharmony_ci		mapping_set_error(mapping, ret);
6478c2ecf20Sopenharmony_ci		break;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
6518c2ecf20Sopenharmony_ci	return ret;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci/*
6558c2ecf20Sopenharmony_ci * write a page back to the server
6568c2ecf20Sopenharmony_ci * - the caller locked the page for us
6578c2ecf20Sopenharmony_ci */
6588c2ecf20Sopenharmony_ciint afs_writepage(struct page *page, struct writeback_control *wbc)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	int ret;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	_enter("{%lx},", page->index);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	ret = afs_write_back_from_locked_page(page->mapping, wbc, page,
6658c2ecf20Sopenharmony_ci					      wbc->range_end >> PAGE_SHIFT);
6668c2ecf20Sopenharmony_ci	if (ret < 0) {
6678c2ecf20Sopenharmony_ci		_leave(" = %d", ret);
6688c2ecf20Sopenharmony_ci		return 0;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	wbc->nr_to_write -= ret;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	_leave(" = 0");
6748c2ecf20Sopenharmony_ci	return 0;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci/*
6788c2ecf20Sopenharmony_ci * write a region of pages back to the server
6798c2ecf20Sopenharmony_ci */
6808c2ecf20Sopenharmony_cistatic int afs_writepages_region(struct address_space *mapping,
6818c2ecf20Sopenharmony_ci				 struct writeback_control *wbc,
6828c2ecf20Sopenharmony_ci				 pgoff_t index, pgoff_t end, pgoff_t *_next)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	struct page *page;
6858c2ecf20Sopenharmony_ci	int ret, n;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	_enter(",,%lx,%lx,", index, end);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	do {
6908c2ecf20Sopenharmony_ci		n = find_get_pages_range_tag(mapping, &index, end,
6918c2ecf20Sopenharmony_ci					PAGECACHE_TAG_DIRTY, 1, &page);
6928c2ecf20Sopenharmony_ci		if (!n)
6938c2ecf20Sopenharmony_ci			break;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		_debug("wback %lx", page->index);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		/*
6988c2ecf20Sopenharmony_ci		 * at this point we hold neither the i_pages lock nor the
6998c2ecf20Sopenharmony_ci		 * page lock: the page may be truncated or invalidated
7008c2ecf20Sopenharmony_ci		 * (changing page->mapping to NULL), or even swizzled
7018c2ecf20Sopenharmony_ci		 * back from swapper_space to tmpfs file mapping
7028c2ecf20Sopenharmony_ci		 */
7038c2ecf20Sopenharmony_ci		ret = lock_page_killable(page);
7048c2ecf20Sopenharmony_ci		if (ret < 0) {
7058c2ecf20Sopenharmony_ci			put_page(page);
7068c2ecf20Sopenharmony_ci			_leave(" = %d", ret);
7078c2ecf20Sopenharmony_ci			return ret;
7088c2ecf20Sopenharmony_ci		}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci		if (page->mapping != mapping || !PageDirty(page)) {
7118c2ecf20Sopenharmony_ci			unlock_page(page);
7128c2ecf20Sopenharmony_ci			put_page(page);
7138c2ecf20Sopenharmony_ci			continue;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci		if (PageWriteback(page)) {
7178c2ecf20Sopenharmony_ci			unlock_page(page);
7188c2ecf20Sopenharmony_ci			if (wbc->sync_mode != WB_SYNC_NONE)
7198c2ecf20Sopenharmony_ci				wait_on_page_writeback(page);
7208c2ecf20Sopenharmony_ci			put_page(page);
7218c2ecf20Sopenharmony_ci			continue;
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		if (!clear_page_dirty_for_io(page))
7258c2ecf20Sopenharmony_ci			BUG();
7268c2ecf20Sopenharmony_ci		ret = afs_write_back_from_locked_page(mapping, wbc, page, end);
7278c2ecf20Sopenharmony_ci		put_page(page);
7288c2ecf20Sopenharmony_ci		if (ret < 0) {
7298c2ecf20Sopenharmony_ci			_leave(" = %d", ret);
7308c2ecf20Sopenharmony_ci			return ret;
7318c2ecf20Sopenharmony_ci		}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		wbc->nr_to_write -= ret;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		cond_resched();
7368c2ecf20Sopenharmony_ci	} while (index < end && wbc->nr_to_write > 0);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	*_next = index;
7398c2ecf20Sopenharmony_ci	_leave(" = 0 [%lx]", *_next);
7408c2ecf20Sopenharmony_ci	return 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci/*
7448c2ecf20Sopenharmony_ci * write some of the pending data back to the server
7458c2ecf20Sopenharmony_ci */
7468c2ecf20Sopenharmony_ciint afs_writepages(struct address_space *mapping,
7478c2ecf20Sopenharmony_ci		   struct writeback_control *wbc)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
7508c2ecf20Sopenharmony_ci	pgoff_t start, end, next;
7518c2ecf20Sopenharmony_ci	int ret;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	_enter("");
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* We have to be careful as we can end up racing with setattr()
7568c2ecf20Sopenharmony_ci	 * truncating the pagecache since the caller doesn't take a lock here
7578c2ecf20Sopenharmony_ci	 * to prevent it.
7588c2ecf20Sopenharmony_ci	 */
7598c2ecf20Sopenharmony_ci	if (wbc->sync_mode == WB_SYNC_ALL)
7608c2ecf20Sopenharmony_ci		down_read(&vnode->validate_lock);
7618c2ecf20Sopenharmony_ci	else if (!down_read_trylock(&vnode->validate_lock))
7628c2ecf20Sopenharmony_ci		return 0;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (wbc->range_cyclic) {
7658c2ecf20Sopenharmony_ci		start = mapping->writeback_index;
7668c2ecf20Sopenharmony_ci		end = -1;
7678c2ecf20Sopenharmony_ci		ret = afs_writepages_region(mapping, wbc, start, end, &next);
7688c2ecf20Sopenharmony_ci		if (start > 0 && wbc->nr_to_write > 0 && ret == 0)
7698c2ecf20Sopenharmony_ci			ret = afs_writepages_region(mapping, wbc, 0, start,
7708c2ecf20Sopenharmony_ci						    &next);
7718c2ecf20Sopenharmony_ci		mapping->writeback_index = next;
7728c2ecf20Sopenharmony_ci	} else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) {
7738c2ecf20Sopenharmony_ci		end = (pgoff_t)(LLONG_MAX >> PAGE_SHIFT);
7748c2ecf20Sopenharmony_ci		ret = afs_writepages_region(mapping, wbc, 0, end, &next);
7758c2ecf20Sopenharmony_ci		if (wbc->nr_to_write > 0)
7768c2ecf20Sopenharmony_ci			mapping->writeback_index = next;
7778c2ecf20Sopenharmony_ci	} else {
7788c2ecf20Sopenharmony_ci		start = wbc->range_start >> PAGE_SHIFT;
7798c2ecf20Sopenharmony_ci		end = wbc->range_end >> PAGE_SHIFT;
7808c2ecf20Sopenharmony_ci		ret = afs_writepages_region(mapping, wbc, start, end, &next);
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	up_read(&vnode->validate_lock);
7848c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
7858c2ecf20Sopenharmony_ci	return ret;
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci/*
7898c2ecf20Sopenharmony_ci * write to an AFS file
7908c2ecf20Sopenharmony_ci */
7918c2ecf20Sopenharmony_cissize_t afs_file_write(struct kiocb *iocb, struct iov_iter *from)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(iocb->ki_filp));
7948c2ecf20Sopenharmony_ci	ssize_t result;
7958c2ecf20Sopenharmony_ci	size_t count = iov_iter_count(from);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},{%zu},",
7988c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, count);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (IS_SWAPFILE(&vnode->vfs_inode)) {
8018c2ecf20Sopenharmony_ci		printk(KERN_INFO
8028c2ecf20Sopenharmony_ci		       "AFS: Attempt to write to active swap file!\n");
8038c2ecf20Sopenharmony_ci		return -EBUSY;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (!count)
8078c2ecf20Sopenharmony_ci		return 0;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	result = generic_file_write_iter(iocb, from);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	_leave(" = %zd", result);
8128c2ecf20Sopenharmony_ci	return result;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci/*
8168c2ecf20Sopenharmony_ci * flush any dirty pages for this process, and check for write errors.
8178c2ecf20Sopenharmony_ci * - the return status from this call provides a reliable indication of
8188c2ecf20Sopenharmony_ci *   whether any write errors occurred for this process.
8198c2ecf20Sopenharmony_ci */
8208c2ecf20Sopenharmony_ciint afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
8238c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	_enter("{%llx:%llu},{n=%pD},%d",
8268c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, file,
8278c2ecf20Sopenharmony_ci	       datasync);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	return file_write_and_wait_range(file, start, end);
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci/*
8338c2ecf20Sopenharmony_ci * notification that a previously read-only page is about to become writable
8348c2ecf20Sopenharmony_ci * - if it returns an error, the caller will deliver a bus error signal
8358c2ecf20Sopenharmony_ci */
8368c2ecf20Sopenharmony_civm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct file *file = vmf->vma->vm_file;
8398c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
8408c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
8418c2ecf20Sopenharmony_ci	unsigned long priv;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	_enter("{{%llx:%llu}},{%lx}",
8448c2ecf20Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, vmf->page->index);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	sb_start_pagefault(inode->i_sb);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	/* Wait for the page to be written to the cache before we allow it to
8498c2ecf20Sopenharmony_ci	 * be modified.  We then assume the entire page will need writing back.
8508c2ecf20Sopenharmony_ci	 */
8518c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
8528c2ecf20Sopenharmony_ci	fscache_wait_on_page_write(vnode->cache, vmf->page);
8538c2ecf20Sopenharmony_ci#endif
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (PageWriteback(vmf->page) &&
8568c2ecf20Sopenharmony_ci	    wait_on_page_bit_killable(vmf->page, PG_writeback) < 0)
8578c2ecf20Sopenharmony_ci		return VM_FAULT_RETRY;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (lock_page_killable(vmf->page) < 0)
8608c2ecf20Sopenharmony_ci		return VM_FAULT_RETRY;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/* We mustn't change page->private until writeback is complete as that
8638c2ecf20Sopenharmony_ci	 * details the portion of the page we need to write back and we might
8648c2ecf20Sopenharmony_ci	 * need to redirty the page if there's a problem.
8658c2ecf20Sopenharmony_ci	 */
8668c2ecf20Sopenharmony_ci	wait_on_page_writeback(vmf->page);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	priv = afs_page_dirty(0, PAGE_SIZE);
8698c2ecf20Sopenharmony_ci	priv = afs_page_dirty_mmapped(priv);
8708c2ecf20Sopenharmony_ci	trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"),
8718c2ecf20Sopenharmony_ci			     vmf->page->index, priv);
8728c2ecf20Sopenharmony_ci	if (PagePrivate(vmf->page))
8738c2ecf20Sopenharmony_ci		set_page_private(vmf->page, priv);
8748c2ecf20Sopenharmony_ci	else
8758c2ecf20Sopenharmony_ci		attach_page_private(vmf->page, (void *)priv);
8768c2ecf20Sopenharmony_ci	file_update_time(file);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	sb_end_pagefault(inode->i_sb);
8798c2ecf20Sopenharmony_ci	return VM_FAULT_LOCKED;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci/*
8838c2ecf20Sopenharmony_ci * Prune the keys cached for writeback.  The caller must hold vnode->wb_lock.
8848c2ecf20Sopenharmony_ci */
8858c2ecf20Sopenharmony_civoid afs_prune_wb_keys(struct afs_vnode *vnode)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	LIST_HEAD(graveyard);
8888c2ecf20Sopenharmony_ci	struct afs_wb_key *wbk, *tmp;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/* Discard unused keys */
8918c2ecf20Sopenharmony_ci	spin_lock(&vnode->wb_lock);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	if (!mapping_tagged(&vnode->vfs_inode.i_data, PAGECACHE_TAG_WRITEBACK) &&
8948c2ecf20Sopenharmony_ci	    !mapping_tagged(&vnode->vfs_inode.i_data, PAGECACHE_TAG_DIRTY)) {
8958c2ecf20Sopenharmony_ci		list_for_each_entry_safe(wbk, tmp, &vnode->wb_keys, vnode_link) {
8968c2ecf20Sopenharmony_ci			if (refcount_read(&wbk->usage) == 1)
8978c2ecf20Sopenharmony_ci				list_move(&wbk->vnode_link, &graveyard);
8988c2ecf20Sopenharmony_ci		}
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	spin_unlock(&vnode->wb_lock);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	while (!list_empty(&graveyard)) {
9048c2ecf20Sopenharmony_ci		wbk = list_entry(graveyard.next, struct afs_wb_key, vnode_link);
9058c2ecf20Sopenharmony_ci		list_del(&wbk->vnode_link);
9068c2ecf20Sopenharmony_ci		afs_put_wb_key(wbk);
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci/*
9118c2ecf20Sopenharmony_ci * Clean up a page during invalidation.
9128c2ecf20Sopenharmony_ci */
9138c2ecf20Sopenharmony_ciint afs_launder_page(struct page *page)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	struct address_space *mapping = page->mapping;
9168c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
9178c2ecf20Sopenharmony_ci	unsigned long priv;
9188c2ecf20Sopenharmony_ci	unsigned int f, t;
9198c2ecf20Sopenharmony_ci	int ret = 0;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	_enter("{%lx}", page->index);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	priv = page_private(page);
9248c2ecf20Sopenharmony_ci	if (clear_page_dirty_for_io(page)) {
9258c2ecf20Sopenharmony_ci		f = 0;
9268c2ecf20Sopenharmony_ci		t = PAGE_SIZE;
9278c2ecf20Sopenharmony_ci		if (PagePrivate(page)) {
9288c2ecf20Sopenharmony_ci			f = afs_page_dirty_from(priv);
9298c2ecf20Sopenharmony_ci			t = afs_page_dirty_to(priv);
9308c2ecf20Sopenharmony_ci		}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		trace_afs_page_dirty(vnode, tracepoint_string("launder"),
9338c2ecf20Sopenharmony_ci				     page->index, priv);
9348c2ecf20Sopenharmony_ci		ret = afs_store_data(mapping, page->index, page->index, t, f, true);
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	priv = (unsigned long)detach_page_private(page);
9388c2ecf20Sopenharmony_ci	trace_afs_page_dirty(vnode, tracepoint_string("laundered"),
9398c2ecf20Sopenharmony_ci			     page->index, priv);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
9428c2ecf20Sopenharmony_ci	if (PageFsCache(page)) {
9438c2ecf20Sopenharmony_ci		fscache_wait_on_page_write(vnode->cache, page);
9448c2ecf20Sopenharmony_ci		fscache_uncache_page(vnode->cache, page);
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci#endif
9478c2ecf20Sopenharmony_ci	return ret;
9488c2ecf20Sopenharmony_ci}
949