mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	afs: convert to new aops
Cannot assume writes will fully complete, so this conversion goes the easy way and always brings the page uptodate before the write. [dhowells@redhat.com: style tweaks] Signed-off-by: Nick Piggin <npiggin@suse.de> Acked-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									8360e81b5d
								
							
						
					
					
						commit
						15b4650e55
					
				
					 3 changed files with 51 additions and 92 deletions
				
			
		| 
						 | 
					@ -50,8 +50,8 @@ const struct address_space_operations afs_fs_aops = {
 | 
				
			||||||
	.launder_page	= afs_launder_page,
 | 
						.launder_page	= afs_launder_page,
 | 
				
			||||||
	.releasepage	= afs_releasepage,
 | 
						.releasepage	= afs_releasepage,
 | 
				
			||||||
	.invalidatepage	= afs_invalidatepage,
 | 
						.invalidatepage	= afs_invalidatepage,
 | 
				
			||||||
	.prepare_write	= afs_prepare_write,
 | 
						.write_begin	= afs_write_begin,
 | 
				
			||||||
	.commit_write	= afs_commit_write,
 | 
						.write_end	= afs_write_end,
 | 
				
			||||||
	.writepage	= afs_writepage,
 | 
						.writepage	= afs_writepage,
 | 
				
			||||||
	.writepages	= afs_writepages,
 | 
						.writepages	= afs_writepages,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -728,8 +728,12 @@ extern int afs_volume_release_fileserver(struct afs_vnode *,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
extern int afs_set_page_dirty(struct page *);
 | 
					extern int afs_set_page_dirty(struct page *);
 | 
				
			||||||
extern void afs_put_writeback(struct afs_writeback *);
 | 
					extern void afs_put_writeback(struct afs_writeback *);
 | 
				
			||||||
extern int afs_prepare_write(struct file *, struct page *, unsigned, unsigned);
 | 
					extern int afs_write_begin(struct file *file, struct address_space *mapping,
 | 
				
			||||||
extern int afs_commit_write(struct file *, struct page *, unsigned, unsigned);
 | 
								loff_t pos, unsigned len, unsigned flags,
 | 
				
			||||||
 | 
								struct page **pagep, void **fsdata);
 | 
				
			||||||
 | 
					extern int afs_write_end(struct file *file, struct address_space *mapping,
 | 
				
			||||||
 | 
								loff_t pos, unsigned len, unsigned copied,
 | 
				
			||||||
 | 
								struct page *page, void *fsdata);
 | 
				
			||||||
extern int afs_writepage(struct page *, struct writeback_control *);
 | 
					extern int afs_writepage(struct page *, struct writeback_control *);
 | 
				
			||||||
extern int afs_writepages(struct address_space *, struct writeback_control *);
 | 
					extern int afs_writepages(struct address_space *, struct writeback_control *);
 | 
				
			||||||
extern int afs_write_inode(struct inode *, int);
 | 
					extern int afs_write_inode(struct inode *, int);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										131
									
								
								fs/afs/write.c
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								fs/afs/write.c
									
									
									
									
									
								
							| 
						 | 
					@ -84,15 +84,23 @@ void afs_put_writeback(struct afs_writeback *wb)
 | 
				
			||||||
 * partly or wholly fill a page that's under preparation for writing
 | 
					 * partly or wholly fill a page that's under preparation for writing
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
 | 
					static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
 | 
				
			||||||
			 unsigned start, unsigned len, struct page *page)
 | 
								 loff_t pos, unsigned len, struct page *page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						loff_t i_size;
 | 
				
			||||||
 | 
						unsigned eof;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter(",,%u,%u", start, len);
 | 
						_enter(",,%llu,%u", (unsigned long long)pos, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERTCMP(start + len, <=, PAGE_SIZE);
 | 
						ASSERTCMP(len, <=, PAGE_CACHE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = afs_vnode_fetch_data(vnode, key, start, len, page);
 | 
						i_size = i_size_read(&vnode->vfs_inode);
 | 
				
			||||||
 | 
						if (pos + len > i_size)
 | 
				
			||||||
 | 
							eof = i_size;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							eof = PAGE_CACHE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = afs_vnode_fetch_data(vnode, key, 0, eof, page);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		if (ret == -ENOENT) {
 | 
							if (ret == -ENOENT) {
 | 
				
			||||||
			_debug("got NOENT from server"
 | 
								_debug("got NOENT from server"
 | 
				
			||||||
| 
						 | 
					@ -106,110 +114,56 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * prepare a page for being written to
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static int afs_prepare_page(struct afs_vnode *vnode, struct page *page,
 | 
					 | 
				
			||||||
			    struct key *key, unsigned offset, unsigned to)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned eof, tail, start, stop, len;
 | 
					 | 
				
			||||||
	loff_t i_size, pos;
 | 
					 | 
				
			||||||
	void *p;
 | 
					 | 
				
			||||||
	int ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_enter("");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (offset == 0 && to == PAGE_SIZE)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p = kmap_atomic(page, KM_USER0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	i_size = i_size_read(&vnode->vfs_inode);
 | 
					 | 
				
			||||||
	pos = (loff_t) page->index << PAGE_SHIFT;
 | 
					 | 
				
			||||||
	if (pos >= i_size) {
 | 
					 | 
				
			||||||
		/* partial write, page beyond EOF */
 | 
					 | 
				
			||||||
		_debug("beyond");
 | 
					 | 
				
			||||||
		if (offset > 0)
 | 
					 | 
				
			||||||
			memset(p, 0, offset);
 | 
					 | 
				
			||||||
		if (to < PAGE_SIZE)
 | 
					 | 
				
			||||||
			memset(p + to, 0, PAGE_SIZE - to);
 | 
					 | 
				
			||||||
		kunmap_atomic(p, KM_USER0);
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (i_size - pos >= PAGE_SIZE) {
 | 
					 | 
				
			||||||
		/* partial write, page entirely before EOF */
 | 
					 | 
				
			||||||
		_debug("before");
 | 
					 | 
				
			||||||
		tail = eof = PAGE_SIZE;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* partial write, page overlaps EOF */
 | 
					 | 
				
			||||||
		eof = i_size - pos;
 | 
					 | 
				
			||||||
		_debug("overlap %u", eof);
 | 
					 | 
				
			||||||
		tail = max(eof, to);
 | 
					 | 
				
			||||||
		if (tail < PAGE_SIZE)
 | 
					 | 
				
			||||||
			memset(p + tail, 0, PAGE_SIZE - tail);
 | 
					 | 
				
			||||||
		if (offset > eof)
 | 
					 | 
				
			||||||
			memset(p + eof, 0, PAGE_SIZE - eof);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	kunmap_atomic(p, KM_USER0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = 0;
 | 
					 | 
				
			||||||
	if (offset > 0 || eof > to) {
 | 
					 | 
				
			||||||
		/* need to fill one or two bits that aren't going to be written
 | 
					 | 
				
			||||||
		 * (cover both fillers in one read if there are two) */
 | 
					 | 
				
			||||||
		start = (offset > 0) ? 0 : to;
 | 
					 | 
				
			||||||
		stop = (eof > to) ? eof : offset;
 | 
					 | 
				
			||||||
		len = stop - start;
 | 
					 | 
				
			||||||
		_debug("wr=%u-%u av=0-%u rd=%u@%u",
 | 
					 | 
				
			||||||
		       offset, to, eof, start, len);
 | 
					 | 
				
			||||||
		ret = afs_fill_page(vnode, key, start, len, page);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = %d", ret);
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * prepare to perform part of a write to a page
 | 
					 * prepare to perform part of a write to a page
 | 
				
			||||||
 * - the caller holds the page locked, preventing it from being written out or
 | 
					 | 
				
			||||||
 *   modified by anyone else
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_prepare_write(struct file *file, struct page *page,
 | 
					int afs_write_begin(struct file *file, struct address_space *mapping,
 | 
				
			||||||
		      unsigned offset, unsigned to)
 | 
							    loff_t pos, unsigned len, unsigned flags,
 | 
				
			||||||
 | 
							    struct page **pagep, void **fsdata)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_writeback *candidate, *wb;
 | 
						struct afs_writeback *candidate, *wb;
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode);
 | 
						struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode);
 | 
				
			||||||
 | 
						struct page *page;
 | 
				
			||||||
	struct key *key = file->private_data;
 | 
						struct key *key = file->private_data;
 | 
				
			||||||
	pgoff_t index;
 | 
						unsigned from = pos & (PAGE_CACHE_SIZE - 1);
 | 
				
			||||||
 | 
						unsigned to = from + len;
 | 
				
			||||||
 | 
						pgoff_t index = pos >> PAGE_CACHE_SHIFT;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%x:%u},{%lx},%u,%u",
 | 
						_enter("{%x:%u},{%lx},%u,%u",
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, page->index, offset, to);
 | 
						       vnode->fid.vid, vnode->fid.vnode, index, from, to);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	candidate = kzalloc(sizeof(*candidate), GFP_KERNEL);
 | 
						candidate = kzalloc(sizeof(*candidate), GFP_KERNEL);
 | 
				
			||||||
	if (!candidate)
 | 
						if (!candidate)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	candidate->vnode = vnode;
 | 
						candidate->vnode = vnode;
 | 
				
			||||||
	candidate->first = candidate->last = page->index;
 | 
						candidate->first = candidate->last = index;
 | 
				
			||||||
	candidate->offset_first = offset;
 | 
						candidate->offset_first = from;
 | 
				
			||||||
	candidate->to_last = to;
 | 
						candidate->to_last = to;
 | 
				
			||||||
	candidate->usage = 1;
 | 
						candidate->usage = 1;
 | 
				
			||||||
	candidate->state = AFS_WBACK_PENDING;
 | 
						candidate->state = AFS_WBACK_PENDING;
 | 
				
			||||||
	init_waitqueue_head(&candidate->waitq);
 | 
						init_waitqueue_head(&candidate->waitq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						page = __grab_cache_page(mapping, index);
 | 
				
			||||||
 | 
						if (!page) {
 | 
				
			||||||
 | 
							kfree(candidate);
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*pagep = page;
 | 
				
			||||||
 | 
						/* page won't leak in error case: it eventually gets cleaned off LRU */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!PageUptodate(page)) {
 | 
						if (!PageUptodate(page)) {
 | 
				
			||||||
		_debug("not up to date");
 | 
							_debug("not up to date");
 | 
				
			||||||
		ret = afs_prepare_page(vnode, page, key, offset, to);
 | 
							ret = afs_fill_page(vnode, key, pos, len, page);
 | 
				
			||||||
		if (ret < 0) {
 | 
							if (ret < 0) {
 | 
				
			||||||
			kfree(candidate);
 | 
								kfree(candidate);
 | 
				
			||||||
			_leave(" = %d [prep]", ret);
 | 
								_leave(" = %d [prep]", ret);
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							SetPageUptodate(page);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try_again:
 | 
					try_again:
 | 
				
			||||||
	index = page->index;
 | 
					 | 
				
			||||||
	spin_lock(&vnode->writeback_lock);
 | 
						spin_lock(&vnode->writeback_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* see if this page is already pending a writeback under a suitable key
 | 
						/* see if this page is already pending a writeback under a suitable key
 | 
				
			||||||
| 
						 | 
					@ -242,8 +196,8 @@ int afs_prepare_write(struct file *file, struct page *page,
 | 
				
			||||||
subsume_in_current_wb:
 | 
					subsume_in_current_wb:
 | 
				
			||||||
	_debug("subsume");
 | 
						_debug("subsume");
 | 
				
			||||||
	ASSERTRANGE(wb->first, <=, index, <=, wb->last);
 | 
						ASSERTRANGE(wb->first, <=, index, <=, wb->last);
 | 
				
			||||||
	if (index == wb->first && offset < wb->offset_first)
 | 
						if (index == wb->first && from < wb->offset_first)
 | 
				
			||||||
		wb->offset_first = offset;
 | 
							wb->offset_first = from;
 | 
				
			||||||
	if (index == wb->last && to > wb->to_last)
 | 
						if (index == wb->last && to > wb->to_last)
 | 
				
			||||||
		wb->to_last = to;
 | 
							wb->to_last = to;
 | 
				
			||||||
	spin_unlock(&vnode->writeback_lock);
 | 
						spin_unlock(&vnode->writeback_lock);
 | 
				
			||||||
| 
						 | 
					@ -289,17 +243,17 @@ int afs_prepare_write(struct file *file, struct page *page,
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * finalise part of a write to a page
 | 
					 * finalise part of a write to a page
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_commit_write(struct file *file, struct page *page,
 | 
					int afs_write_end(struct file *file, struct address_space *mapping,
 | 
				
			||||||
		     unsigned offset, unsigned to)
 | 
							  loff_t pos, unsigned len, unsigned copied,
 | 
				
			||||||
 | 
							  struct page *page, void *fsdata)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode);
 | 
						struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode);
 | 
				
			||||||
	loff_t i_size, maybe_i_size;
 | 
						loff_t i_size, maybe_i_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%x:%u},{%lx},%u,%u",
 | 
						_enter("{%x:%u},{%lx}",
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, page->index, offset, to);
 | 
						       vnode->fid.vid, vnode->fid.vnode, page->index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	maybe_i_size = (loff_t) page->index << PAGE_SHIFT;
 | 
						maybe_i_size = pos + copied;
 | 
				
			||||||
	maybe_i_size += to;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	i_size = i_size_read(&vnode->vfs_inode);
 | 
						i_size = i_size_read(&vnode->vfs_inode);
 | 
				
			||||||
	if (maybe_i_size > i_size) {
 | 
						if (maybe_i_size > i_size) {
 | 
				
			||||||
| 
						 | 
					@ -310,12 +264,13 @@ int afs_commit_write(struct file *file, struct page *page,
 | 
				
			||||||
		spin_unlock(&vnode->writeback_lock);
 | 
							spin_unlock(&vnode->writeback_lock);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SetPageUptodate(page);
 | 
					 | 
				
			||||||
	set_page_dirty(page);
 | 
						set_page_dirty(page);
 | 
				
			||||||
	if (PageDirty(page))
 | 
						if (PageDirty(page))
 | 
				
			||||||
		_debug("dirtied");
 | 
							_debug("dirtied");
 | 
				
			||||||
 | 
						unlock_page(page);
 | 
				
			||||||
 | 
						page_cache_release(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return copied;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue