diff options
Diffstat (limited to 'fs/afs/write.c')
| -rw-r--r-- | fs/afs/write.c | 105 | 
1 files changed, 55 insertions, 50 deletions
| diff --git a/fs/afs/write.c b/fs/afs/write.c index da12abd6db21..50371207f327 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -76,7 +76,7 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,   */  int afs_write_begin(struct file *file, struct address_space *mapping,  		    loff_t pos, unsigned len, unsigned flags, -		    struct page **pagep, void **fsdata) +		    struct page **_page, void **fsdata)  {  	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));  	struct page *page; @@ -90,11 +90,6 @@ int afs_write_begin(struct file *file, struct address_space *mapping,  	_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; @@ -110,9 +105,6 @@ int afs_write_begin(struct file *file, struct address_space *mapping,  		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. @@ -120,8 +112,8 @@ try_again:  	t = f = 0;  	if (PagePrivate(page)) {  		priv = page_private(page); -		f = priv & AFS_PRIV_MAX; -		t = priv >> AFS_PRIV_SHIFT; +		f = afs_page_dirty_from(priv); +		t = afs_page_dirty_to(priv);  		ASSERTCMP(f, <=, t);  	} @@ -138,21 +130,9 @@ try_again:  		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); +	*_page = page;  	_leave(" = 0");  	return 0; @@ -162,17 +142,18 @@ try_again:  flush_conflicting_write:  	_debug("flush conflict");  	ret = write_one_page(page); -	if (ret < 0) { -		_leave(" = %d", ret); -		return ret; -	} +	if (ret < 0) +		goto error;  	ret = lock_page_killable(page); -	if (ret < 0) { -		_leave(" = %d", ret); -		return ret; -	} +	if (ret < 0) +		goto error;  	goto try_again; + +error: +	put_page(page); +	_leave(" = %d", ret); +	return ret;  }  /* @@ -184,6 +165,9 @@ int afs_write_end(struct file *file, struct address_space *mapping,  {  	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));  	struct key *key = afs_file_key(file); +	unsigned long priv; +	unsigned int f, from = pos & (PAGE_SIZE - 1); +	unsigned int t, to = from + copied;  	loff_t i_size, maybe_i_size;  	int ret; @@ -215,6 +199,25 @@ int afs_write_end(struct file *file, struct address_space *mapping,  		SetPageUptodate(page);  	} +	if (PagePrivate(page)) { +		priv = page_private(page); +		f = afs_page_dirty_from(priv); +		t = afs_page_dirty_to(priv); +		if (from < f) +			f = from; +		if (to > t) +			t = to; +		priv = afs_page_dirty(f, t); +		set_page_private(page, priv); +		trace_afs_page_dirty(vnode, tracepoint_string("dirty+"), +				     page->index, priv); +	} else { +		priv = afs_page_dirty(from, to); +		attach_page_private(page, (void *)priv); +		trace_afs_page_dirty(vnode, tracepoint_string("dirty"), +				     page->index, priv); +	} +  	set_page_dirty(page);  	if (PageDirty(page))  		_debug("dirtied"); @@ -334,10 +337,9 @@ static void afs_pages_written_back(struct afs_vnode *vnode,  		ASSERTCMP(pv.nr, ==, count);  		for (loop = 0; loop < count; loop++) { -			priv = page_private(pv.pages[loop]); +			priv = (unsigned long)detach_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; @@ -396,7 +398,8 @@ static void afs_store_data_success(struct afs_operation *op)  	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); +		if (!op->store.laundering) +			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), @@ -415,7 +418,7 @@ static const struct afs_operation_ops afs_store_data_operation = {   */  static int afs_store_data(struct address_space *mapping,  			  pgoff_t first, pgoff_t last, -			  unsigned offset, unsigned to) +			  unsigned offset, unsigned to, bool laundering)  {  	struct afs_vnode *vnode = AFS_FS_I(mapping->host);  	struct afs_operation *op; @@ -448,6 +451,7 @@ static int afs_store_data(struct address_space *mapping,  	op->store.last = last;  	op->store.first_offset = offset;  	op->store.last_to = to; +	op->store.laundering = laundering;  	op->mtime = vnode->vfs_inode.i_mtime;  	op->flags |= AFS_OPERATION_UNINTR;  	op->ops = &afs_store_data_operation; @@ -509,8 +513,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,  	 */  	start = primary_page->index;  	priv = page_private(primary_page); -	offset = priv & AFS_PRIV_MAX; -	to = priv >> AFS_PRIV_SHIFT; +	offset = afs_page_dirty_from(priv); +	to = afs_page_dirty_to(priv);  	trace_afs_page_dirty(vnode, tracepoint_string("store"),  			     primary_page->index, priv); @@ -555,8 +559,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,  			}  			priv = page_private(page); -			f = priv & AFS_PRIV_MAX; -			t = priv >> AFS_PRIV_SHIFT; +			f = afs_page_dirty_from(priv); +			t = afs_page_dirty_to(priv);  			if (f != 0 &&  			    !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) {  				unlock_page(page); @@ -601,7 +605,7 @@ no_more:  	if (end > i_size)  		to = i_size & ~PAGE_MASK; -	ret = afs_store_data(mapping, first, last, offset, to); +	ret = afs_store_data(mapping, first, last, offset, to, false);  	switch (ret) {  	case 0:  		ret = count; @@ -857,12 +861,14 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)  	 */  	wait_on_page_writeback(vmf->page); -	priv = (unsigned long)PAGE_SIZE << AFS_PRIV_SHIFT; /* To */ -	priv |= 0; /* From */ +	priv = afs_page_dirty(0, PAGE_SIZE); +	priv = afs_page_dirty_mmapped(priv);  	trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"),  			     vmf->page->index, priv); -	SetPagePrivate(vmf->page); -	set_page_private(vmf->page, priv); +	if (PagePrivate(vmf->page)) +		set_page_private(vmf->page, priv); +	else +		attach_page_private(vmf->page, (void *)priv);  	file_update_time(file);  	sb_end_pagefault(inode->i_sb); @@ -915,19 +921,18 @@ int afs_launder_page(struct page *page)  		f = 0;  		t = PAGE_SIZE;  		if (PagePrivate(page)) { -			f = priv & AFS_PRIV_MAX; -			t = priv >> AFS_PRIV_SHIFT; +			f = afs_page_dirty_from(priv); +			t = afs_page_dirty_to(priv);  		}  		trace_afs_page_dirty(vnode, tracepoint_string("launder"),  				     page->index, priv); -		ret = afs_store_data(mapping, page->index, page->index, t, f); +		ret = afs_store_data(mapping, page->index, page->index, t, f, true);  	} +	priv = (unsigned long)detach_page_private(page);  	trace_afs_page_dirty(vnode, tracepoint_string("laundered"),  			     page->index, priv); -	set_page_private(page, 0); -	ClearPagePrivate(page);  #ifdef CONFIG_AFS_FSCACHE  	if (PageFsCache(page)) { |