960 lines · 22.6 KB · C
RawDownload
// 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.