928 lines · 21.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 "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);} /* * partly or wholly fill a page that's under preparation for writing */static int afs_fill_page(struct afs_vnode *vnode, struct key *key,			 loff_t pos, unsigned int len, struct page *page){	struct afs_read *req;	size_t p;	void *data;	int ret; 	_enter(",,%llu", (unsigned long long)pos); 	if (pos >= vnode->vfs_inode.i_size) {		p = pos & ~PAGE_MASK;		ASSERTCMP(p + len, <=, PAGE_SIZE);		data = kmap(page);		memset(data + p, 0, len);		kunmap(page);		return 0;	} 	req = kzalloc(struct_size(req, array, 1), GFP_KERNEL);	if (!req)		return -ENOMEM; 	refcount_set(&req->usage, 1);	req->pos = pos;	req->len = len;	req->nr_pages = 1;	req->pages = req->array;	req->pages[0] = page;	get_page(page); 	ret = afs_fetch_data(vnode, key, req);	afs_put_read(req);	if (ret < 0) {		if (ret == -ENOENT) {			_debug("got NOENT from server"			       " - marking file deleted and stale");			set_bit(AFS_VNODE_DELETED, &vnode->flags);			ret = -ESTALE;		}	} 	_leave(" = %d", ret);	return ret;} /* * 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 **pagep, void **fsdata){	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));	struct page *page;	struct key *key = afs_file_key(file);	unsigned long priv;	unsigned f, from = pos & (PAGE_SIZE - 1);	unsigned t, to = from + len;	pgoff_t index = pos >> PAGE_SHIFT;	int ret; 	_enter("{%llx:%llu},{%lx},%u,%u",	       vnode->fid.vid, vnode->fid.vnode, index, from, to); 	/* We want to store information about how much of a page is altered in	 * page->private.	 */	BUILD_BUG_ON(PAGE_SIZE > 32768 && sizeof(page->private) < 8); 	page = grab_cache_page_write_begin(mapping, index, flags);	if (!page)		return -ENOMEM; 	if (!PageUptodate(page) && len != PAGE_SIZE) {		ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page);		if (ret < 0) {			unlock_page(page);			put_page(page);			_leave(" = %d [prep]", ret);			return ret;		}		SetPageUptodate(page);	} 	/* page won't leak in error case: it eventually gets cleaned off LRU */	*pagep = page; try_again:	/* See if this page is already partially written in a way that we can	 * merge the new write with.	 */	t = f = 0;	if (PagePrivate(page)) {		priv = page_private(page);		f = priv & AFS_PRIV_MAX;		t = priv >> AFS_PRIV_SHIFT;		ASSERTCMP(f, <=, t);	} 	if (f != t) {		if (PageWriteback(page)) {			trace_afs_page_dirty(vnode, tracepoint_string("alrdy"),					     page->index, priv);			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;		if (from < f)			f = from;		if (to > t)			t = to;	} else {		f = from;		t = to;	} 	priv = (unsigned long)t << AFS_PRIV_SHIFT;	priv |= f;	trace_afs_page_dirty(vnode, tracepoint_string("begin"),			     page->index, priv);	SetPagePrivate(page);	set_page_private(page, priv);	_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) {		_leave(" = %d", ret);		return ret;	} 	ret = lock_page_killable(page);	if (ret < 0) {		_leave(" = %d", ret);		return ret;	}	goto try_again;} /* * 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));	struct key *key = afs_file_key(file);	loff_t i_size, maybe_i_size;	int ret; 	_enter("{%llx:%llu},{%lx}",	       vnode->fid.vid, vnode->fid.vnode, page->index); 	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 (!PageUptodate(page)) {		if (copied < len) {			/* Try and load any missing data from the server.  The			 * unmarshalling routine will take care of clearing any			 * bits that are beyond the EOF.			 */			ret = afs_fill_page(vnode, key, pos + copied,					    len - copied, page);			if (ret < 0)				goto out;		}		SetPageUptodate(page);	} 	set_page_dirty(page);	if (PageDirty(page))		_debug("dirtied");	ret = copied; out:	unlock_page(page);	put_page(page);	return ret;} /* * kill all the pages in the given range */static void afs_kill_pages(struct address_space *mapping,			   pgoff_t first, pgoff_t last){	struct afs_vnode *vnode = AFS_FS_I(mapping->host);	struct pagevec pv;	unsigned count, loop; 	_enter("{%llx:%llu},%lx-%lx",	       vnode->fid.vid, vnode->fid.vnode, first, last); 	pagevec_init(&pv); 	do {		_debug("kill %lx-%lx", first, last); 		count = last - first + 1;		if (count > PAGEVEC_SIZE)			count = PAGEVEC_SIZE;		pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);		ASSERTCMP(pv.nr, ==, count); 		for (loop = 0; loop < count; loop++) {			struct page *page = pv.pages[loop];			ClearPageUptodate(page);			SetPageError(page);			end_page_writeback(page);			if (page->index >= first)				first = page->index + 1;			lock_page(page);			generic_error_remove_page(mapping, page);			unlock_page(page);		} 		__pagevec_release(&pv);	} while (first <= last); 	_leave("");} /* * Redirty all the pages in a given range. */static void afs_redirty_pages(struct writeback_control *wbc,			      struct address_space *mapping,			      pgoff_t first, pgoff_t last){	struct afs_vnode *vnode = AFS_FS_I(mapping->host);	struct pagevec pv;	unsigned count, loop; 	_enter("{%llx:%llu},%lx-%lx",	       vnode->fid.vid, vnode->fid.vnode, first, last); 	pagevec_init(&pv); 	do {		_debug("redirty %lx-%lx", first, last); 		count = last - first + 1;		if (count > PAGEVEC_SIZE)			count = PAGEVEC_SIZE;		pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);		ASSERTCMP(pv.nr, ==, count); 		for (loop = 0; loop < count; loop++) {			struct page *page = pv.pages[loop]; 			redirty_page_for_writepage(wbc, page);			end_page_writeback(page);			if (page->index >= first)				first = page->index + 1;		} 		__pagevec_release(&pv);	} while (first <= last); 	_leave("");} /* * completion of write to server */static void afs_pages_written_back(struct afs_vnode *vnode,				   pgoff_t first, pgoff_t last){	struct pagevec pv;	unsigned long priv;	unsigned count, loop; 	_enter("{%llx:%llu},{%lx-%lx}",	       vnode->fid.vid, vnode->fid.vnode, first, last); 	pagevec_init(&pv); 	do {		_debug("done %lx-%lx", first, last); 		count = last - first + 1;		if (count > PAGEVEC_SIZE)			count = PAGEVEC_SIZE;		pv.nr = find_get_pages_contig(vnode->vfs_inode.i_mapping,					      first, count, pv.pages);		ASSERTCMP(pv.nr, ==, count); 		for (loop = 0; loop < count; loop++) {			priv = page_private(pv.pages[loop]);			trace_afs_page_dirty(vnode, tracepoint_string("clear"),					     pv.pages[loop]->index, priv);			set_page_private(pv.pages[loop], 0);			end_page_writeback(pv.pages[loop]);		}		first += count;		__pagevec_release(&pv);	} while (first <= last); 	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) {		afs_pages_written_back(vnode, op->store.first, op->store.last);		afs_stat_v(vnode, n_stores);		atomic_long_add((op->store.last * PAGE_SIZE + op->store.last_to) -				(op->store.first * PAGE_SIZE + op->store.first_offset),				&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 address_space *mapping,			  pgoff_t first, pgoff_t last,			  unsigned offset, unsigned to){	struct afs_vnode *vnode = AFS_FS_I(mapping->host);	struct afs_operation *op;	struct afs_wb_key *wbk = NULL;	int ret; 	_enter("%s{%llx:%llu.%u},%lx,%lx,%x,%x",	       vnode->volume->name,	       vnode->fid.vid,	       vnode->fid.vnode,	       vnode->fid.unique,	       first, last, offset, to); 	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;	} 	afs_op_set_vnode(op, 0, vnode);	op->file[0].dv_delta = 1;	op->store.mapping = mapping;	op->store.first = first;	op->store.last = last;	op->store.first_offset = offset;	op->store.last_to = to;	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);} /* * Synchronously write back the locked page and any subsequent non-locked dirty * pages. */static int afs_write_back_from_locked_page(struct address_space *mapping,					   struct writeback_control *wbc,					   struct page *primary_page,					   pgoff_t final_page){	struct afs_vnode *vnode = AFS_FS_I(mapping->host);	struct page *pages[8], *page;	unsigned long count, priv;	unsigned n, offset, to, f, t;	pgoff_t start, first, last;	loff_t i_size, end;	int loop, ret; 	_enter(",%lx", primary_page->index); 

Lines 1–500 of 928.