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