// SPDX-License-Identifier: GPL-2.0-or-later/* handling of writes to regular files and writing back to the server * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #include <linux/backing-dev.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/writeback.h>#include <linux/pagevec.h>#include <linux/netfs.h>#include <linux/fscache.h>#include "internal.h" /* * mark a page as having been made dirty and thus needing writeback */int afs_set_page_dirty(struct page *page){ _enter(""); return __set_page_dirty_nobuffers(page);} /* * prepare to perform part of a write to a page */int afs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **_page, void **fsdata){ struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct page *page; unsigned long priv; unsigned f, from; unsigned t, to; pgoff_t index; int ret; _enter("{%llx:%llu},%llx,%x", vnode->fid.vid, vnode->fid.vnode, pos, len); /* Prefetch area to be written into the cache if we're caching this * file. We need to do this before we get a lock on the page in case * there's more than one writer competing for the same cache block. */ ret = netfs_write_begin(file, mapping, pos, len, flags, &page, fsdata, &afs_req_ops, NULL); if (ret < 0) return ret; index = page->index; from = pos - index * PAGE_SIZE; to = from + len; try_again: /* See if this page is already partially written in a way that we can * merge the new write with. */ if (PagePrivate(page)) { priv = page_private(page); f = afs_page_dirty_from(page, priv); t = afs_page_dirty_to(page, priv); ASSERTCMP(f, <=, t); if (PageWriteback(page)) { trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page); goto flush_conflicting_write; } /* If the file is being filled locally, allow inter-write * spaces to be merged into writes. If it's not, only write * back what the user gives us. */ if (!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags) && (to < f || from > t)) goto flush_conflicting_write; } *_page = page; _leave(" = 0"); return 0; /* The previous write and this write aren't adjacent or overlapping, so * flush the page out. */flush_conflicting_write: _debug("flush conflict"); ret = write_one_page(page); if (ret < 0) goto error; ret = lock_page_killable(page); if (ret < 0) goto error; goto try_again; error: put_page(page); _leave(" = %d", ret); return ret;} /* * finalise part of a write to a page */int afs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata){ struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); unsigned long priv; unsigned int f, from = pos & (thp_size(page) - 1); unsigned int t, to = from + copied; loff_t i_size, maybe_i_size; _enter("{%llx:%llu},{%lx}", vnode->fid.vid, vnode->fid.vnode, page->index); if (!PageUptodate(page)) { if (copied < len) { copied = 0; goto out; } SetPageUptodate(page); } if (copied == 0) goto out; maybe_i_size = pos + copied; i_size = i_size_read(&vnode->vfs_inode); if (maybe_i_size > i_size) { write_seqlock(&vnode->cb_lock); i_size = i_size_read(&vnode->vfs_inode); if (maybe_i_size > i_size) i_size_write(&vnode->vfs_inode, maybe_i_size); write_sequnlock(&vnode->cb_lock); } if (PagePrivate(page)) { priv = page_private(page); f = afs_page_dirty_from(page, priv); t = afs_page_dirty_to(page, priv); if (from < f) f = from; if (to > t) t = to; priv = afs_page_dirty(page, f, t); set_page_private(page, priv); trace_afs_page_dirty(vnode, tracepoint_string("dirty+"), page); } else { priv = afs_page_dirty(page, from, to); attach_page_private(page, (void *)priv); trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page); } if (set_page_dirty(page)) _debug("dirtied %lx", page->index); out: unlock_page(page); put_page(page); return copied;} /* * kill all the pages in the given range */static void afs_kill_pages(struct address_space *mapping, loff_t start, loff_t len){ struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct pagevec pv; unsigned int loop, psize; _enter("{%llx:%llu},%llx @%llx", vnode->fid.vid, vnode->fid.vnode, len, start); pagevec_init(&pv); do { _debug("kill %llx @%llx", len, start); pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE, PAGEVEC_SIZE, pv.pages); if (pv.nr == 0) break; for (loop = 0; loop < pv.nr; loop++) { struct page *page = pv.pages[loop]; if (page->index * PAGE_SIZE >= start + len) break; psize = thp_size(page); start += psize; len -= psize; ClearPageUptodate(page); end_page_writeback(page); lock_page(page); generic_error_remove_page(mapping, page); unlock_page(page); } __pagevec_release(&pv); } while (len > 0); _leave("");} /* * Redirty all the pages in a given range. */static void afs_redirty_pages(struct writeback_control *wbc, struct address_space *mapping, loff_t start, loff_t len){ struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct pagevec pv; unsigned int loop, psize; _enter("{%llx:%llu},%llx @%llx", vnode->fid.vid, vnode->fid.vnode, len, start); pagevec_init(&pv); do { _debug("redirty %llx @%llx", len, start); pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE, PAGEVEC_SIZE, pv.pages); if (pv.nr == 0) break; for (loop = 0; loop < pv.nr; loop++) { struct page *page = pv.pages[loop]; if (page->index * PAGE_SIZE >= start + len) break; psize = thp_size(page); start += psize; len -= psize; redirty_page_for_writepage(wbc, page); end_page_writeback(page); } __pagevec_release(&pv); } while (len > 0); _leave("");} /* * completion of write to server */static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsigned int len){ struct address_space *mapping = vnode->vfs_inode.i_mapping; struct page *page; pgoff_t end; XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE); _enter("{%llx:%llu},{%x @%llx}", vnode->fid.vid, vnode->fid.vnode, len, start); rcu_read_lock(); end = (start + len - 1) / PAGE_SIZE; xas_for_each(&xas, page, end) { if (!PageWriteback(page)) { kdebug("bad %x @%llx page %lx %lx", len, start, page->index, end); ASSERT(PageWriteback(page)); } trace_afs_page_dirty(vnode, tracepoint_string("clear"), page); detach_page_private(page); page_endio(page, true, 0); } rcu_read_unlock(); afs_prune_wb_keys(vnode); _leave("");} /* * Find a key to use for the writeback. We cached the keys used to author the * writes on the vnode. *_wbk will contain the last writeback key used or NULL * and we need to start from there if it's set. */static int afs_get_writeback_key(struct afs_vnode *vnode, struct afs_wb_key **_wbk){ struct afs_wb_key *wbk = NULL; struct list_head *p; int ret = -ENOKEY, ret2; spin_lock(&vnode->wb_lock); if (*_wbk) p = (*_wbk)->vnode_link.next; else p = vnode->wb_keys.next; while (p != &vnode->wb_keys) { wbk = list_entry(p, struct afs_wb_key, vnode_link); _debug("wbk %u", key_serial(wbk->key)); ret2 = key_validate(wbk->key); if (ret2 == 0) { refcount_inc(&wbk->usage); _debug("USE WB KEY %u", key_serial(wbk->key)); break; } wbk = NULL; if (ret == -ENOKEY) ret = ret2; p = p->next; } spin_unlock(&vnode->wb_lock); if (*_wbk) afs_put_wb_key(*_wbk); *_wbk = wbk; return 0;} static void afs_store_data_success(struct afs_operation *op){ struct afs_vnode *vnode = op->file[0].vnode; op->ctime = op->file[0].scb.status.mtime_client; afs_vnode_commit_status(op, &op->file[0]); if (op->error == 0) { if (!op->store.laundering) afs_pages_written_back(vnode, op->store.pos, op->store.size); afs_stat_v(vnode, n_stores); atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes); }} static const struct afs_operation_ops afs_store_data_operation = { .issue_afs_rpc = afs_fs_store_data, .issue_yfs_rpc = yfs_fs_store_data, .success = afs_store_data_success,}; /* * write to a file */static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos, bool laundering){ struct afs_operation *op; struct afs_wb_key *wbk = NULL; loff_t size = iov_iter_count(iter), i_size; int ret = -ENOKEY; _enter("%s{%llx:%llu.%u},%llx,%llx", vnode->volume->name, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, size, pos); ret = afs_get_writeback_key(vnode, &wbk); if (ret) { _leave(" = %d [no keys]", ret); return ret; } op = afs_alloc_operation(wbk->key, vnode->volume); if (IS_ERR(op)) { afs_put_wb_key(wbk); return -ENOMEM; } i_size = i_size_read(&vnode->vfs_inode); afs_op_set_vnode(op, 0, vnode); op->file[0].dv_delta = 1; op->file[0].modification = true; op->store.write_iter = iter; op->store.pos = pos; op->store.size = size; op->store.i_size = max(pos + size, i_size); op->store.laundering = laundering; op->mtime = vnode->vfs_inode.i_mtime; op->flags |= AFS_OPERATION_UNINTR; op->ops = &afs_store_data_operation; try_next_key: afs_begin_vnode_operation(op); afs_wait_for_operation(op); switch (op->error) { case -EACCES: case -EPERM: case -ENOKEY: case -EKEYEXPIRED: case -EKEYREJECTED: case -EKEYREVOKED: _debug("next"); ret = afs_get_writeback_key(vnode, &wbk); if (ret == 0) { key_put(op->key); op->key = key_get(wbk->key); goto try_next_key; } break; } afs_put_wb_key(wbk); _leave(" = %d", op->error); return afs_put_operation(op);} /* * Extend the region to be written back to include subsequent contiguously * dirty pages if possible, but don't sleep while doing so. * * If this page holds new content, then we can include filler zeros in the * writeback. */static void afs_extend_writeback(struct address_space *mapping, struct afs_vnode *vnode, long *_count, loff_t start, loff_t max_len, bool new_content, unsigned int *_len){ struct pagevec pvec; struct page *page; unsigned long priv; unsigned int psize, filler = 0; unsigned int f, t; loff_t len = *_len; pgoff_t index = (start + len) / PAGE_SIZE; bool stop = true; unsigned int i; XA_STATE(xas, &mapping->i_pages, index); pagevec_init(&pvec); do { /* Firstly, we gather up a batch of contiguous dirty pages * under the RCU read lock - but we can't clear the dirty flags * there if any of those pages are mapped. */ rcu_read_lock(); xas_for_each(&xas, page, ULONG_MAX) { stop = true; if (xas_retry(&xas, page)) continue; if (xa_is_value(page)) break; if (page->index != index) break; if (!page_cache_get_speculative(page)) { xas_reset(&xas); continue; } /* Has the page moved or been split? */ if (unlikely(page != xas_reload(&xas))) break; if (!trylock_page(page)) break; if (!PageDirty(page) || PageWriteback(page)) { unlock_page(page); break; } psize = thp_size(page); priv = page_private(page); f = afs_page_dirty_from(page, priv); t = afs_page_dirty_to(page, priv); if (f != 0 && !new_content) { unlock_page(page); break; } len += filler + t; filler = psize - t; if (len >= max_len || *_count <= 0) stop = true; else if (t == psize || new_content) stop = false; index += thp_nr_pages(page);Lines 1–500 of 960. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* handling of writes to regular files and writing back to the server * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #include <linux/backing-dev.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/pagemap.h> #include <linux/writeback.h> #include <linux/pagevec.h> #include <linux/netfs.h> #include <linux/fscache.h> #include "internal.h" /* * mark a page as having been made dirty and thus needing writeback */ int afs_set_page_dirty(struct page *page) { _enter(""); return __set_page_dirty_nobuffers(page); } /* * prepare to perform part of a write to a page */ int afs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **_page, void **fsdata) { struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct page *page; unsigned long priv; unsigned f, from; unsigned t, to; pgoff_t index; int ret; _enter("{%llx:%llu},%llx,%x", vnode->fid.vid, vnode->fid.vnode, pos, len); /* Prefetch area to be written into the cache if we're caching this * file. We need to do this before we get a lock on the page in case * there's more than one writer competing for the same cache block. */ ret = netfs_write_begin(file, mapping, pos, len, flags, &page, fsdata, &afs_req_ops, NULL); if (ret < 0) return ret; index = page->index; from = pos - index * PAGE_SIZE; to = from + len; try_again: /* See if this page is already partially written in a way that we can * merge the new write with. */ if (PagePrivate(page)) { priv = page_private(page); f = afs_page_dirty_from(page, priv); t = afs_page_dirty_to(page, priv); ASSERTCMP(f, <=, t); if (PageWriteback(page)) { trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page); goto flush_conflicting_write; } /* If the file is being filled locally, allow inter-write * spaces to be merged into writes. If it's not, only write * back what the user gives us. */ if (!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags) && (to < f || from > t)) goto flush_conflicting_write; } *_page = page; _leave(" = 0"); return 0; /* The previous write and this write aren't adjacent or overlapping, so * flush the page out. */ flush_conflicting_write: _debug("flush conflict"); ret = write_one_page(page); if (ret < 0) goto error; ret = lock_page_killable(page); if (ret < 0) goto error; goto try_again; error: put_page(page); _leave(" = %d", ret); return ret; } /* * finalise part of a write to a page */ int afs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) { struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); unsigned long priv; unsigned int f, from = pos & (thp_size(page) - 1); unsigned int t, to = from + copied; loff_t i_size, maybe_i_size; _enter("{%llx:%llu},{%lx}", vnode->fid.vid, vnode->fid.vnode, page->index); if (!PageUptodate(page)) { if (copied < len) { copied = 0; goto out; } SetPageUptodate(page); } if (copied == 0) goto out; maybe_i_size = pos + copied; i_size = i_size_read(&vnode->vfs_inode); if (maybe_i_size > i_size) { write_seqlock(&vnode->cb_lock); i_size = i_size_read(&vnode->vfs_inode); if (maybe_i_size > i_size) i_size_write(&vnode->vfs_inode, maybe_i_size); write_sequnlock(&vnode->cb_lock); } if (PagePrivate(page)) { priv = page_private(page); f = afs_page_dirty_from(page, priv); t = afs_page_dirty_to(page, priv); if (from < f) f = from; if (to > t) t = to; priv = afs_page_dirty(page, f, t); set_page_private(page, priv); trace_afs_page_dirty(vnode, tracepoint_string("dirty+"), page); } else { priv = afs_page_dirty(page, from, to); attach_page_private(page, (void *)priv); trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page); } if (set_page_dirty(page)) _debug("dirtied %lx", page->index); out: unlock_page(page); put_page(page); return copied; } /* * kill all the pages in the given range */ static void afs_kill_pages(struct address_space *mapping, loff_t start, loff_t len) { struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct pagevec pv; unsigned int loop, psize; _enter("{%llx:%llu},%llx @%llx", vnode->fid.vid, vnode->fid.vnode, len, start); pagevec_init(&pv); do { _debug("kill %llx @%llx", len, start); pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE, PAGEVEC_SIZE, pv.pages); if (pv.nr == 0) break; for (loop = 0; loop < pv.nr; loop++) { struct page *page = pv.pages[loop]; if (page->index * PAGE_SIZE >= start + len) break; psize = thp_size(page); start += psize; len -= psize; ClearPageUptodate(page); end_page_writeback(page); lock_page(page); generic_error_remove_page(mapping, page); unlock_page(page); } __pagevec_release(&pv); } while (len > 0); _leave(""); } /* * Redirty all the pages in a given range. */ static void afs_redirty_pages(struct writeback_control *wbc, struct address_space *mapping, loff_t start, loff_t len) { struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct pagevec pv; unsigned int loop, psize; _enter("{%llx:%llu},%llx @%llx", vnode->fid.vid, vnode->fid.vnode, len, start); pagevec_init(&pv); do { _debug("redirty %llx @%llx", len, start); pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE, PAGEVEC_SIZE, pv.pages); if (pv.nr == 0) break; for (loop = 0; loop < pv.nr; loop++) { struct page *page = pv.pages[loop]; if (page->index * PAGE_SIZE >= start + len) break; psize = thp_size(page); start += psize; len -= psize; redirty_page_for_writepage(wbc, page); end_page_writeback(page); } __pagevec_release(&pv); } while (len > 0); _leave(""); } /* * completion of write to server */ static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsigned int len) { struct address_space *mapping = vnode->vfs_inode.i_mapping; struct page *page; pgoff_t end; XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE); _enter("{%llx:%llu},{%x @%llx}", vnode->fid.vid, vnode->fid.vnode, len, start); rcu_read_lock(); end = (start + len - 1) / PAGE_SIZE; xas_for_each(&xas, page, end) { if (!PageWriteback(page)) { kdebug("bad %x @%llx page %lx %lx", len, start, page->index, end); ASSERT(PageWriteback(page)); } trace_afs_page_dirty(vnode, tracepoint_string("clear"), page); detach_page_private(page); page_endio(page, true, 0); } rcu_read_unlock(); afs_prune_wb_keys(vnode); _leave(""); } /* * Find a key to use for the writeback. We cached the keys used to author the * writes on the vnode. *_wbk will contain the last writeback key used or NULL * and we need to start from there if it's set. */ static int afs_get_writeback_key(struct afs_vnode *vnode, struct afs_wb_key **_wbk) { struct afs_wb_key *wbk = NULL; struct list_head *p; int ret = -ENOKEY, ret2; spin_lock(&vnode->wb_lock); if (*_wbk) p = (*_wbk)->vnode_link.next; else p = vnode->wb_keys.next; while (p != &vnode->wb_keys) { wbk = list_entry(p, struct afs_wb_key, vnode_link); _debug("wbk %u", key_serial(wbk->key)); ret2 = key_validate(wbk->key); if (ret2 == 0) { refcount_inc(&wbk->usage); _debug("USE WB KEY %u", key_serial(wbk->key)); break; } wbk = NULL; if (ret == -ENOKEY) ret = ret2; p = p->next; } spin_unlock(&vnode->wb_lock); if (*_wbk) afs_put_wb_key(*_wbk); *_wbk = wbk; return 0; } static void afs_store_data_success(struct afs_operation *op) { struct afs_vnode *vnode = op->file[0].vnode; op->ctime = op->file[0].scb.status.mtime_client; afs_vnode_commit_status(op, &op->file[0]); if (op->error == 0) { if (!op->store.laundering) afs_pages_written_back(vnode, op->store.pos, op->store.size); afs_stat_v(vnode, n_stores); atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes); } } static const struct afs_operation_ops afs_store_data_operation = { .issue_afs_rpc = afs_fs_store_data, .issue_yfs_rpc = yfs_fs_store_data, .success = afs_store_data_success, }; /* * write to a file */ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos, bool laundering) { struct afs_operation *op; struct afs_wb_key *wbk = NULL; loff_t size = iov_iter_count(iter), i_size; int ret = -ENOKEY; _enter("%s{%llx:%llu.%u},%llx,%llx", vnode->volume->name, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, size, pos); ret = afs_get_writeback_key(vnode, &wbk); if (ret) { _leave(" = %d [no keys]", ret); return ret; } op = afs_alloc_operation(wbk->key, vnode->volume); if (IS_ERR(op)) { afs_put_wb_key(wbk); return -ENOMEM; } i_size = i_size_read(&vnode->vfs_inode); afs_op_set_vnode(op, 0, vnode); op->file[0].dv_delta = 1; op->file[0].modification = true; op->store.write_iter = iter; op->store.pos = pos; op->store.size = size; op->store.i_size = max(pos + size, i_size); op->store.laundering = laundering; op->mtime = vnode->vfs_inode.i_mtime; op->flags |= AFS_OPERATION_UNINTR; op->ops = &afs_store_data_operation; try_next_key: afs_begin_vnode_operation(op); afs_wait_for_operation(op); switch (op->error) { case -EACCES: case -EPERM: case -ENOKEY: case -EKEYEXPIRED: case -EKEYREJECTED: case -EKEYREVOKED: _debug("next"); ret = afs_get_writeback_key(vnode, &wbk); if (ret == 0) { key_put(op->key); op->key = key_get(wbk->key); goto try_next_key; } break; } afs_put_wb_key(wbk); _leave(" = %d", op->error); return afs_put_operation(op); } /* * Extend the region to be written back to include subsequent contiguously * dirty pages if possible, but don't sleep while doing so. * * If this page holds new content, then we can include filler zeros in the * writeback. */ static void afs_extend_writeback(struct address_space *mapping, struct afs_vnode *vnode, long *_count, loff_t start, loff_t max_len, bool new_content, unsigned int *_len) { struct pagevec pvec; struct page *page; unsigned long priv; unsigned int psize, filler = 0; unsigned int f, t; loff_t len = *_len; pgoff_t index = (start + len) / PAGE_SIZE; bool stop = true; unsigned int i; XA_STATE(xas, &mapping->i_pages, index); pagevec_init(&pvec); do { /* Firstly, we gather up a batch of contiguous dirty pages * under the RCU read lock - but we can't clear the dirty flags * there if any of those pages are mapped. */ rcu_read_lock(); xas_for_each(&xas, page, ULONG_MAX) { stop = true; if (xas_retry(&xas, page)) continue; if (xa_is_value(page)) break; if (page->index != index) break; if (!page_cache_get_speculative(page)) { xas_reset(&xas); continue; } /* Has the page moved or been split? */ if (unlikely(page != xas_reload(&xas))) break; if (!trylock_page(page)) break; if (!PageDirty(page) || PageWriteback(page)) { unlock_page(page); break; } psize = thp_size(page); priv = page_private(page); f = afs_page_dirty_from(page, priv); t = afs_page_dirty_to(page, priv); if (f != 0 && !new_content) { unlock_page(page); break; } len += filler + t; filler = psize - t; if (len >= max_len || *_count <= 0) stop = true; else if (t == psize || new_content) stop = false; index += thp_nr_pages(page);