mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	afs: Prepare for use of THPs
As a prelude to supporting transparent huge pages, use thp_size() and similar rather than PAGE_SIZE/SHIFT. Further, try and frame everything in terms of file positions and lengths rather than page indices and numbers of pages. Signed-off-by: David Howells <dhowells@redhat.com> Tested-By: Marc Dionne <marc.dionne@auristor.com> cc: linux-afs@lists.infradead.org cc: linux-cachefs@redhat.com cc: linux-fsdevel@vger.kernel.org Link: https://lore.kernel.org/r/160588540227.3465195.4752143929716269062.stgit@warthog.procyon.org.uk/ # rfc Link: https://lore.kernel.org/r/161118155821.1232039.540445038028845740.stgit@warthog.procyon.org.uk/ # rfc Link: https://lore.kernel.org/r/161161051439.2537118.15577827510426326534.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/161340415869.1303470.6040191748634322355.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/161539559365.286939.18344613540296085269.stgit@warthog.procyon.org.uk/ # v4 Link: https://lore.kernel.org/r/161653815142.2770958.454490670311230206.stgit@warthog.procyon.org.uk/ # v5 Link: https://lore.kernel.org/r/161789098713.6155.16394227991842480300.stgit@warthog.procyon.org.uk/ # v6
This commit is contained in:
		
							parent
							
								
									810caa3e67
								
							
						
					
					
						commit
						e87b03f583
					
				
					 4 changed files with 242 additions and 200 deletions
				
			
		| 
						 | 
					@ -2083,6 +2083,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
 | 
				
			||||||
		afs_stat_v(dvnode, n_inval);
 | 
							afs_stat_v(dvnode, n_inval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* we clean up only if the entire page is being invalidated */
 | 
						/* we clean up only if the entire page is being invalidated */
 | 
				
			||||||
	if (offset == 0 && length == PAGE_SIZE)
 | 
						if (offset == 0 && length == thp_size(page))
 | 
				
			||||||
		detach_page_private(page);
 | 
							detach_page_private(page);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -329,8 +329,8 @@ static int afs_page_filler(struct key *key, struct page *page)
 | 
				
			||||||
	req->vnode		= vnode;
 | 
						req->vnode		= vnode;
 | 
				
			||||||
	req->key		= key_get(key);
 | 
						req->key		= key_get(key);
 | 
				
			||||||
	req->pos		= (loff_t)page->index << PAGE_SHIFT;
 | 
						req->pos		= (loff_t)page->index << PAGE_SHIFT;
 | 
				
			||||||
	req->len		= PAGE_SIZE;
 | 
						req->len		= thp_size(page);
 | 
				
			||||||
	req->nr_pages		= 1;
 | 
						req->nr_pages		= thp_nr_pages(page);
 | 
				
			||||||
	req->done		= afs_file_read_done;
 | 
						req->done		= afs_file_read_done;
 | 
				
			||||||
	req->cleanup		= afs_file_read_cleanup;
 | 
						req->cleanup		= afs_file_read_cleanup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -574,8 +574,8 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset,
 | 
				
			||||||
	trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page);
 | 
						trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page);
 | 
				
			||||||
	clear_page_dirty_for_io(page);
 | 
						clear_page_dirty_for_io(page);
 | 
				
			||||||
full_invalidate:
 | 
					full_invalidate:
 | 
				
			||||||
	detach_page_private(page);
 | 
					 | 
				
			||||||
	trace_afs_page_dirty(vnode, tracepoint_string("inval"), page);
 | 
						trace_afs_page_dirty(vnode, tracepoint_string("inval"), page);
 | 
				
			||||||
 | 
						detach_page_private(page);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -620,8 +620,8 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (PagePrivate(page)) {
 | 
						if (PagePrivate(page)) {
 | 
				
			||||||
		detach_page_private(page);
 | 
					 | 
				
			||||||
		trace_afs_page_dirty(vnode, tracepoint_string("rel"), page);
 | 
							trace_afs_page_dirty(vnode, tracepoint_string("rel"), page);
 | 
				
			||||||
 | 
							detach_page_private(page);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* indicate that the page can be released */
 | 
						/* indicate that the page can be released */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -815,8 +815,6 @@ struct afs_operation {
 | 
				
			||||||
			loff_t	pos;
 | 
								loff_t	pos;
 | 
				
			||||||
			loff_t	size;
 | 
								loff_t	size;
 | 
				
			||||||
			loff_t	i_size;
 | 
								loff_t	i_size;
 | 
				
			||||||
			pgoff_t	first;		/* first page in mapping to deal with */
 | 
					 | 
				
			||||||
			pgoff_t	last;		/* last page in mapping to deal with */
 | 
					 | 
				
			||||||
			bool	laundering;	/* Laundering page, PG_writeback not set */
 | 
								bool	laundering;	/* Laundering page, PG_writeback not set */
 | 
				
			||||||
		} store;
 | 
							} store;
 | 
				
			||||||
		struct {
 | 
							struct {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										416
									
								
								fs/afs/write.c
									
									
									
									
									
								
							
							
						
						
									
										416
									
								
								fs/afs/write.c
									
									
									
									
									
								
							| 
						 | 
					@ -94,15 +94,15 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
 | 
						struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
 | 
				
			||||||
	struct page *page;
 | 
						struct page *page;
 | 
				
			||||||
	unsigned long priv;
 | 
						unsigned long priv;
 | 
				
			||||||
	unsigned f, from = pos & (PAGE_SIZE - 1);
 | 
						unsigned f, from;
 | 
				
			||||||
	unsigned t, to = from + len;
 | 
						unsigned t, to;
 | 
				
			||||||
	pgoff_t index = pos >> PAGE_SHIFT;
 | 
						pgoff_t index;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%llx:%llu},{%lx},%u,%u",
 | 
						_enter("{%llx:%llu},%llx,%x",
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, index, from, to);
 | 
						       vnode->fid.vid, vnode->fid.vnode, pos, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	page = grab_cache_page_write_begin(mapping, index, flags);
 | 
						page = grab_cache_page_write_begin(mapping, pos / PAGE_SIZE, flags);
 | 
				
			||||||
	if (!page)
 | 
						if (!page)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,19 +121,20 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
 | 
				
			||||||
	wait_on_page_fscache(page);
 | 
						wait_on_page_fscache(page);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						index = page->index;
 | 
				
			||||||
 | 
						from = pos - index * PAGE_SIZE;
 | 
				
			||||||
 | 
						to = from + len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try_again:
 | 
					try_again:
 | 
				
			||||||
	/* See if this page is already partially written in a way that we can
 | 
						/* See if this page is already partially written in a way that we can
 | 
				
			||||||
	 * merge the new write with.
 | 
						 * merge the new write with.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	t = f = 0;
 | 
					 | 
				
			||||||
	if (PagePrivate(page)) {
 | 
						if (PagePrivate(page)) {
 | 
				
			||||||
		priv = page_private(page);
 | 
							priv = page_private(page);
 | 
				
			||||||
		f = afs_page_dirty_from(page, priv);
 | 
							f = afs_page_dirty_from(page, priv);
 | 
				
			||||||
		t = afs_page_dirty_to(page, priv);
 | 
							t = afs_page_dirty_to(page, priv);
 | 
				
			||||||
		ASSERTCMP(f, <=, t);
 | 
							ASSERTCMP(f, <=, t);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (f != t) {
 | 
					 | 
				
			||||||
		if (PageWriteback(page)) {
 | 
							if (PageWriteback(page)) {
 | 
				
			||||||
			trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page);
 | 
								trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page);
 | 
				
			||||||
			goto flush_conflicting_write;
 | 
								goto flush_conflicting_write;
 | 
				
			||||||
| 
						 | 
					@ -180,7 +181,7 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
 | 
						struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
 | 
				
			||||||
	unsigned long priv;
 | 
						unsigned long priv;
 | 
				
			||||||
	unsigned int f, from = pos & (PAGE_SIZE - 1);
 | 
						unsigned int f, from = pos & (thp_size(page) - 1);
 | 
				
			||||||
	unsigned int t, to = from + copied;
 | 
						unsigned int t, to = from + copied;
 | 
				
			||||||
	loff_t i_size, maybe_i_size;
 | 
						loff_t i_size, maybe_i_size;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
| 
						 | 
					@ -233,9 +234,8 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 | 
				
			||||||
		trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page);
 | 
							trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set_page_dirty(page);
 | 
						if (set_page_dirty(page))
 | 
				
			||||||
	if (PageDirty(page))
 | 
							_debug("dirtied %lx", page->index);
 | 
				
			||||||
		_debug("dirtied");
 | 
					 | 
				
			||||||
	ret = copied;
 | 
						ret = copied;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
| 
						 | 
					@ -248,40 +248,43 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 | 
				
			||||||
 * kill all the pages in the given range
 | 
					 * kill all the pages in the given range
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_kill_pages(struct address_space *mapping,
 | 
					static void afs_kill_pages(struct address_space *mapping,
 | 
				
			||||||
			   pgoff_t first, pgoff_t last)
 | 
								   loff_t start, loff_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
						struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
				
			||||||
	struct pagevec pv;
 | 
						struct pagevec pv;
 | 
				
			||||||
	unsigned count, loop;
 | 
						unsigned int loop, psize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%llx:%llu},%lx-%lx",
 | 
						_enter("{%llx:%llu},%llx @%llx",
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, first, last);
 | 
						       vnode->fid.vid, vnode->fid.vnode, len, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pagevec_init(&pv);
 | 
						pagevec_init(&pv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		_debug("kill %lx-%lx", first, last);
 | 
							_debug("kill %llx @%llx", len, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		count = last - first + 1;
 | 
							pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE,
 | 
				
			||||||
		if (count > PAGEVEC_SIZE)
 | 
										      PAGEVEC_SIZE, pv.pages);
 | 
				
			||||||
			count = PAGEVEC_SIZE;
 | 
							if (pv.nr == 0)
 | 
				
			||||||
		pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
 | 
								break;
 | 
				
			||||||
		ASSERTCMP(pv.nr, ==, count);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (loop = 0; loop < count; loop++) {
 | 
							for (loop = 0; loop < pv.nr; loop++) {
 | 
				
			||||||
			struct page *page = pv.pages[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);
 | 
								ClearPageUptodate(page);
 | 
				
			||||||
			SetPageError(page);
 | 
					 | 
				
			||||||
			end_page_writeback(page);
 | 
								end_page_writeback(page);
 | 
				
			||||||
			if (page->index >= first)
 | 
					 | 
				
			||||||
				first = page->index + 1;
 | 
					 | 
				
			||||||
			lock_page(page);
 | 
								lock_page(page);
 | 
				
			||||||
			generic_error_remove_page(mapping, page);
 | 
								generic_error_remove_page(mapping, page);
 | 
				
			||||||
			unlock_page(page);
 | 
								unlock_page(page);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		__pagevec_release(&pv);
 | 
							__pagevec_release(&pv);
 | 
				
			||||||
	} while (first <= last);
 | 
						} while (len > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave("");
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -291,37 +294,40 @@ static void afs_kill_pages(struct address_space *mapping,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_redirty_pages(struct writeback_control *wbc,
 | 
					static void afs_redirty_pages(struct writeback_control *wbc,
 | 
				
			||||||
			      struct address_space *mapping,
 | 
								      struct address_space *mapping,
 | 
				
			||||||
			      pgoff_t first, pgoff_t last)
 | 
								      loff_t start, loff_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
						struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
				
			||||||
	struct pagevec pv;
 | 
						struct pagevec pv;
 | 
				
			||||||
	unsigned count, loop;
 | 
						unsigned int loop, psize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%llx:%llu},%lx-%lx",
 | 
						_enter("{%llx:%llu},%llx @%llx",
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, first, last);
 | 
						       vnode->fid.vid, vnode->fid.vnode, len, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pagevec_init(&pv);
 | 
						pagevec_init(&pv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		_debug("redirty %lx-%lx", first, last);
 | 
							_debug("redirty %llx @%llx", len, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		count = last - first + 1;
 | 
							pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE,
 | 
				
			||||||
		if (count > PAGEVEC_SIZE)
 | 
										      PAGEVEC_SIZE, pv.pages);
 | 
				
			||||||
			count = PAGEVEC_SIZE;
 | 
							if (pv.nr == 0)
 | 
				
			||||||
		pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
 | 
								break;
 | 
				
			||||||
		ASSERTCMP(pv.nr, ==, count);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (loop = 0; loop < count; loop++) {
 | 
							for (loop = 0; loop < pv.nr; loop++) {
 | 
				
			||||||
			struct page *page = pv.pages[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);
 | 
								redirty_page_for_writepage(wbc, page);
 | 
				
			||||||
			end_page_writeback(page);
 | 
								end_page_writeback(page);
 | 
				
			||||||
			if (page->index >= first)
 | 
					 | 
				
			||||||
				first = page->index + 1;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		__pagevec_release(&pv);
 | 
							__pagevec_release(&pv);
 | 
				
			||||||
	} while (first <= last);
 | 
						} while (len > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_leave("");
 | 
						_leave("");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -329,23 +335,28 @@ static void afs_redirty_pages(struct writeback_control *wbc,
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * completion of write to server
 | 
					 * completion of write to server
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void afs_pages_written_back(struct afs_vnode *vnode, pgoff_t start, pgoff_t last)
 | 
					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 address_space *mapping = vnode->vfs_inode.i_mapping;
 | 
				
			||||||
	struct page *page;
 | 
						struct page *page;
 | 
				
			||||||
 | 
						pgoff_t end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	XA_STATE(xas, &mapping->i_pages, start);
 | 
						XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%llx:%llu},{%lx-%lx}",
 | 
						_enter("{%llx:%llu},{%x @%llx}",
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, start, last);
 | 
						       vnode->fid.vid, vnode->fid.vnode, len, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rcu_read_lock();
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xas_for_each(&xas, page, last) {
 | 
						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));
 | 
								ASSERT(PageWriteback(page));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		detach_page_private(page);
 | 
					 | 
				
			||||||
		trace_afs_page_dirty(vnode, tracepoint_string("clear"), page);
 | 
							trace_afs_page_dirty(vnode, tracepoint_string("clear"), page);
 | 
				
			||||||
 | 
							detach_page_private(page);
 | 
				
			||||||
		page_endio(page, true, 0);
 | 
							page_endio(page, true, 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -404,7 +415,7 @@ static void afs_store_data_success(struct afs_operation *op)
 | 
				
			||||||
	afs_vnode_commit_status(op, &op->file[0]);
 | 
						afs_vnode_commit_status(op, &op->file[0]);
 | 
				
			||||||
	if (op->error == 0) {
 | 
						if (op->error == 0) {
 | 
				
			||||||
		if (!op->store.laundering)
 | 
							if (!op->store.laundering)
 | 
				
			||||||
			afs_pages_written_back(vnode, op->store.first, op->store.last);
 | 
								afs_pages_written_back(vnode, op->store.pos, op->store.size);
 | 
				
			||||||
		afs_stat_v(vnode, n_stores);
 | 
							afs_stat_v(vnode, n_stores);
 | 
				
			||||||
		atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes);
 | 
							atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -419,8 +430,7 @@ static const struct afs_operation_ops afs_store_data_operation = {
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * write to a file
 | 
					 * write to a file
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
 | 
					static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos,
 | 
				
			||||||
			  loff_t pos, pgoff_t first, pgoff_t last,
 | 
					 | 
				
			||||||
			  bool laundering)
 | 
								  bool laundering)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_operation *op;
 | 
						struct afs_operation *op;
 | 
				
			||||||
| 
						 | 
					@ -453,8 +463,6 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
 | 
				
			||||||
	op->file[0].dv_delta = 1;
 | 
						op->file[0].dv_delta = 1;
 | 
				
			||||||
	op->store.write_iter = iter;
 | 
						op->store.write_iter = iter;
 | 
				
			||||||
	op->store.pos = pos;
 | 
						op->store.pos = pos;
 | 
				
			||||||
	op->store.first = first;
 | 
					 | 
				
			||||||
	op->store.last = last;
 | 
					 | 
				
			||||||
	op->store.size = size;
 | 
						op->store.size = size;
 | 
				
			||||||
	op->store.i_size = max(pos + size, i_size);
 | 
						op->store.i_size = max(pos + size, i_size);
 | 
				
			||||||
	op->store.laundering = laundering;
 | 
						op->store.laundering = laundering;
 | 
				
			||||||
| 
						 | 
					@ -499,40 +507,49 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
 | 
				
			||||||
static void afs_extend_writeback(struct address_space *mapping,
 | 
					static void afs_extend_writeback(struct address_space *mapping,
 | 
				
			||||||
				 struct afs_vnode *vnode,
 | 
									 struct afs_vnode *vnode,
 | 
				
			||||||
				 long *_count,
 | 
									 long *_count,
 | 
				
			||||||
				 pgoff_t start,
 | 
									 loff_t start,
 | 
				
			||||||
				 pgoff_t final_page,
 | 
									 loff_t max_len,
 | 
				
			||||||
				 unsigned *_offset,
 | 
									 bool new_content,
 | 
				
			||||||
				 unsigned *_to,
 | 
									 unsigned int *_len)
 | 
				
			||||||
				 bool new_content)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct page *pages[8], *page;
 | 
						struct pagevec pvec;
 | 
				
			||||||
	unsigned long count = *_count, priv;
 | 
						struct page *page;
 | 
				
			||||||
	unsigned offset = *_offset, to = *_to, n, f, t;
 | 
						unsigned long priv;
 | 
				
			||||||
	int loop;
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	start++;
 | 
					 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		_debug("more %lx [%lx]", start, count);
 | 
							/* Firstly, we gather up a batch of contiguous dirty pages
 | 
				
			||||||
		n = final_page - start + 1;
 | 
							 * under the RCU read lock - but we can't clear the dirty flags
 | 
				
			||||||
		if (n > ARRAY_SIZE(pages))
 | 
							 * there if any of those pages are mapped.
 | 
				
			||||||
			n = ARRAY_SIZE(pages);
 | 
							 */
 | 
				
			||||||
		n = find_get_pages_contig(mapping, start, ARRAY_SIZE(pages), pages);
 | 
							rcu_read_lock();
 | 
				
			||||||
		_debug("fgpc %u", n);
 | 
					
 | 
				
			||||||
		if (n == 0)
 | 
							xas_for_each(&xas, page, ULONG_MAX) {
 | 
				
			||||||
			goto no_more;
 | 
								stop = true;
 | 
				
			||||||
		if (pages[0]->index != start) {
 | 
								if (xas_retry(&xas, page))
 | 
				
			||||||
			do {
 | 
									continue;
 | 
				
			||||||
				put_page(pages[--n]);
 | 
								if (xa_is_value(page))
 | 
				
			||||||
			} while (n > 0);
 | 
									break;
 | 
				
			||||||
			goto no_more;
 | 
								if (page->index != index)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!page_cache_get_speculative(page)) {
 | 
				
			||||||
 | 
									xas_reset(&xas);
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (loop = 0; loop < n; loop++) {
 | 
								/* Has the page moved or been split? */
 | 
				
			||||||
			page = pages[loop];
 | 
								if (unlikely(page != xas_reload(&xas)))
 | 
				
			||||||
			if (to != PAGE_SIZE && !new_content)
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			if (page->index > final_page)
 | 
					 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!trylock_page(page))
 | 
								if (!trylock_page(page))
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			if (!PageDirty(page) || PageWriteback(page)) {
 | 
								if (!PageDirty(page) || PageWriteback(page)) {
 | 
				
			||||||
| 
						 | 
					@ -540,6 +557,7 @@ static void afs_extend_writeback(struct address_space *mapping,
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								psize = thp_size(page);
 | 
				
			||||||
			priv = page_private(page);
 | 
								priv = page_private(page);
 | 
				
			||||||
			f = afs_page_dirty_from(page, priv);
 | 
								f = afs_page_dirty_from(page, priv);
 | 
				
			||||||
			t = afs_page_dirty_to(page, priv);
 | 
								t = afs_page_dirty_to(page, priv);
 | 
				
			||||||
| 
						 | 
					@ -547,110 +565,126 @@ static void afs_extend_writeback(struct address_space *mapping,
 | 
				
			||||||
				unlock_page(page);
 | 
									unlock_page(page);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			to = t;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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);
 | 
				
			||||||
 | 
								if (!pagevec_add(&pvec, page))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								if (stop)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!stop)
 | 
				
			||||||
 | 
								xas_pause(&xas);
 | 
				
			||||||
 | 
							rcu_read_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Now, if we obtained any pages, we can shift them to being
 | 
				
			||||||
 | 
							 * writable and mark them for caching.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!pagevec_count(&pvec))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < pagevec_count(&pvec); i++) {
 | 
				
			||||||
 | 
								page = pvec.pages[i];
 | 
				
			||||||
			trace_afs_page_dirty(vnode, tracepoint_string("store+"), page);
 | 
								trace_afs_page_dirty(vnode, tracepoint_string("store+"), page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!clear_page_dirty_for_io(page))
 | 
								if (!clear_page_dirty_for_io(page))
 | 
				
			||||||
				BUG();
 | 
									BUG();
 | 
				
			||||||
			if (test_set_page_writeback(page))
 | 
								if (test_set_page_writeback(page))
 | 
				
			||||||
				BUG();
 | 
									BUG();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								*_count -= thp_nr_pages(page);
 | 
				
			||||||
			unlock_page(page);
 | 
								unlock_page(page);
 | 
				
			||||||
			put_page(page);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		count += loop;
 | 
					 | 
				
			||||||
		if (loop < n) {
 | 
					 | 
				
			||||||
			for (; loop < n; loop++)
 | 
					 | 
				
			||||||
				put_page(pages[loop]);
 | 
					 | 
				
			||||||
			goto no_more;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		start += loop;
 | 
							pagevec_release(&pvec);
 | 
				
			||||||
	} while (start <= final_page && count < 65536);
 | 
							cond_resched();
 | 
				
			||||||
 | 
						} while (!stop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
no_more:
 | 
						*_len = len;
 | 
				
			||||||
	*_count = count;
 | 
					 | 
				
			||||||
	*_offset = offset;
 | 
					 | 
				
			||||||
	*_to = to;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Synchronously write back the locked page and any subsequent non-locked dirty
 | 
					 * Synchronously write back the locked page and any subsequent non-locked dirty
 | 
				
			||||||
 * pages.
 | 
					 * pages.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_write_back_from_locked_page(struct address_space *mapping,
 | 
					static ssize_t afs_write_back_from_locked_page(struct address_space *mapping,
 | 
				
			||||||
					       struct writeback_control *wbc,
 | 
										       struct writeback_control *wbc,
 | 
				
			||||||
					   struct page *primary_page,
 | 
										       struct page *page,
 | 
				
			||||||
					   pgoff_t final_page)
 | 
										       loff_t start, loff_t end)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
						struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
				
			||||||
	struct iov_iter iter;
 | 
						struct iov_iter iter;
 | 
				
			||||||
	unsigned long count, priv;
 | 
						unsigned long priv;
 | 
				
			||||||
	unsigned offset, to;
 | 
						unsigned int offset, to, len, max_len;
 | 
				
			||||||
	pgoff_t start, first, last;
 | 
						loff_t i_size = i_size_read(&vnode->vfs_inode);
 | 
				
			||||||
	loff_t i_size, pos, end;
 | 
					 | 
				
			||||||
	bool new_content = test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
 | 
						bool new_content = test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
 | 
				
			||||||
 | 
						long count = wbc->nr_to_write;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter(",%lx", primary_page->index);
 | 
						_enter(",%lx,%llx-%llx", page->index, start, end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	count = 1;
 | 
						if (test_set_page_writeback(page))
 | 
				
			||||||
	if (test_set_page_writeback(primary_page))
 | 
					 | 
				
			||||||
		BUG();
 | 
							BUG();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						count -= thp_nr_pages(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Find all consecutive lockable dirty pages that have contiguous
 | 
						/* Find all consecutive lockable dirty pages that have contiguous
 | 
				
			||||||
	 * written regions, stopping when we find a page that is not
 | 
						 * written regions, stopping when we find a page that is not
 | 
				
			||||||
	 * immediately lockable, is not dirty or is missing, or we reach the
 | 
						 * immediately lockable, is not dirty or is missing, or we reach the
 | 
				
			||||||
	 * end of the range.
 | 
						 * end of the range.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	start = primary_page->index;
 | 
						priv = page_private(page);
 | 
				
			||||||
	priv = page_private(primary_page);
 | 
						offset = afs_page_dirty_from(page, priv);
 | 
				
			||||||
	offset = afs_page_dirty_from(primary_page, priv);
 | 
						to = afs_page_dirty_to(page, priv);
 | 
				
			||||||
	to = afs_page_dirty_to(primary_page, priv);
 | 
						trace_afs_page_dirty(vnode, tracepoint_string("store"), page);
 | 
				
			||||||
	trace_afs_page_dirty(vnode, tracepoint_string("store"), primary_page);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN_ON(offset == to);
 | 
						len = to - offset;
 | 
				
			||||||
	if (offset == to)
 | 
						start += offset;
 | 
				
			||||||
		trace_afs_page_dirty(vnode, tracepoint_string("WARN"), primary_page);
 | 
						if (start < i_size) {
 | 
				
			||||||
 | 
							/* Trim the write to the EOF; the extra data is ignored.  Also
 | 
				
			||||||
 | 
							 * put an upper limit on the size of a single storedata op.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							max_len = 65536 * 4096;
 | 
				
			||||||
 | 
							max_len = min_t(unsigned long long, max_len, end - start + 1);
 | 
				
			||||||
 | 
							max_len = min_t(unsigned long long, max_len, i_size - start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (start < final_page &&
 | 
							if (len < max_len &&
 | 
				
			||||||
	    (to == PAGE_SIZE || new_content))
 | 
							    (to == thp_size(page) || new_content))
 | 
				
			||||||
		afs_extend_writeback(mapping, vnode, &count, start, final_page,
 | 
								afs_extend_writeback(mapping, vnode, &count,
 | 
				
			||||||
				     &offset, &to, new_content);
 | 
										     start, max_len, new_content, &len);
 | 
				
			||||||
 | 
							len = min_t(loff_t, len, max_len);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We now have a contiguous set of dirty pages, each with writeback
 | 
						/* We now have a contiguous set of dirty pages, each with writeback
 | 
				
			||||||
	 * set; the first page is still locked at this point, but all the rest
 | 
						 * set; the first page is still locked at this point, but all the rest
 | 
				
			||||||
	 * have been unlocked.
 | 
						 * have been unlocked.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	unlock_page(primary_page);
 | 
						unlock_page(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	first = primary_page->index;
 | 
						if (start < i_size) {
 | 
				
			||||||
	last = first + count - 1;
 | 
							_debug("write back %x @%llx [%llx]", len, start, i_size);
 | 
				
			||||||
	_debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pos = first;
 | 
							iov_iter_xarray(&iter, WRITE, &mapping->i_pages, start, len);
 | 
				
			||||||
	pos <<= PAGE_SHIFT;
 | 
							ret = afs_store_data(vnode, &iter, start, false);
 | 
				
			||||||
	pos += offset;
 | 
					 | 
				
			||||||
	end = last;
 | 
					 | 
				
			||||||
	end <<= PAGE_SHIFT;
 | 
					 | 
				
			||||||
	end += to;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Trim the actual write down to the EOF */
 | 
					 | 
				
			||||||
	i_size = i_size_read(&vnode->vfs_inode);
 | 
					 | 
				
			||||||
	if (end > i_size)
 | 
					 | 
				
			||||||
		end = i_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (pos < i_size) {
 | 
					 | 
				
			||||||
		iov_iter_xarray(&iter, WRITE, &mapping->i_pages, pos, end - pos);
 | 
					 | 
				
			||||||
		ret = afs_store_data(vnode, &iter, pos, first, last, false);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
 | 
							_debug("write discard %x @%llx [%llx]", len, start, i_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* The dirty region was entirely beyond the EOF. */
 | 
							/* The dirty region was entirely beyond the EOF. */
 | 
				
			||||||
 | 
							afs_pages_written_back(vnode, start, len);
 | 
				
			||||||
		ret = 0;
 | 
							ret = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (ret) {
 | 
						switch (ret) {
 | 
				
			||||||
	case 0:
 | 
						case 0:
 | 
				
			||||||
		ret = count;
 | 
							wbc->nr_to_write = count;
 | 
				
			||||||
 | 
							ret = len;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
| 
						 | 
					@ -662,13 +696,13 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
 | 
				
			||||||
	case -EKEYEXPIRED:
 | 
						case -EKEYEXPIRED:
 | 
				
			||||||
	case -EKEYREJECTED:
 | 
						case -EKEYREJECTED:
 | 
				
			||||||
	case -EKEYREVOKED:
 | 
						case -EKEYREVOKED:
 | 
				
			||||||
		afs_redirty_pages(wbc, mapping, first, last);
 | 
							afs_redirty_pages(wbc, mapping, start, len);
 | 
				
			||||||
		mapping_set_error(mapping, ret);
 | 
							mapping_set_error(mapping, ret);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case -EDQUOT:
 | 
						case -EDQUOT:
 | 
				
			||||||
	case -ENOSPC:
 | 
						case -ENOSPC:
 | 
				
			||||||
		afs_redirty_pages(wbc, mapping, first, last);
 | 
							afs_redirty_pages(wbc, mapping, start, len);
 | 
				
			||||||
		mapping_set_error(mapping, -ENOSPC);
 | 
							mapping_set_error(mapping, -ENOSPC);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -680,7 +714,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
 | 
				
			||||||
	case -ENOMEDIUM:
 | 
						case -ENOMEDIUM:
 | 
				
			||||||
	case -ENXIO:
 | 
						case -ENXIO:
 | 
				
			||||||
		trace_afs_file_error(vnode, ret, afs_file_error_writeback_fail);
 | 
							trace_afs_file_error(vnode, ret, afs_file_error_writeback_fail);
 | 
				
			||||||
		afs_kill_pages(mapping, first, last);
 | 
							afs_kill_pages(mapping, start, len);
 | 
				
			||||||
		mapping_set_error(mapping, ret);
 | 
							mapping_set_error(mapping, ret);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -695,19 +729,19 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int afs_writepage(struct page *page, struct writeback_control *wbc)
 | 
					int afs_writepage(struct page *page, struct writeback_control *wbc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						ssize_t ret;
 | 
				
			||||||
 | 
						loff_t start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{%lx},", page->index);
 | 
						_enter("{%lx},", page->index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start = page->index * PAGE_SIZE;
 | 
				
			||||||
	ret = afs_write_back_from_locked_page(page->mapping, wbc, page,
 | 
						ret = afs_write_back_from_locked_page(page->mapping, wbc, page,
 | 
				
			||||||
					      wbc->range_end >> PAGE_SHIFT);
 | 
										      start, LLONG_MAX - start);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		_leave(" = %d", ret);
 | 
							_leave(" = %zd", ret);
 | 
				
			||||||
		return 0;
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wbc->nr_to_write -= ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_leave(" = 0");
 | 
						_leave(" = 0");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -717,35 +751,46 @@ int afs_writepage(struct page *page, struct writeback_control *wbc)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int afs_writepages_region(struct address_space *mapping,
 | 
					static int afs_writepages_region(struct address_space *mapping,
 | 
				
			||||||
				 struct writeback_control *wbc,
 | 
									 struct writeback_control *wbc,
 | 
				
			||||||
				 pgoff_t index, pgoff_t end, pgoff_t *_next)
 | 
									 loff_t start, loff_t end, loff_t *_next)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct page *page;
 | 
						struct page *page;
 | 
				
			||||||
	int ret, n;
 | 
						ssize_t ret;
 | 
				
			||||||
 | 
						int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter(",,%lx,%lx,", index, end);
 | 
						_enter("%llx,%llx,", start, end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		n = find_get_pages_range_tag(mapping, &index, end,
 | 
							pgoff_t index = start / PAGE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							n = find_get_pages_range_tag(mapping, &index, end / PAGE_SIZE,
 | 
				
			||||||
					     PAGECACHE_TAG_DIRTY, 1, &page);
 | 
										     PAGECACHE_TAG_DIRTY, 1, &page);
 | 
				
			||||||
		if (!n)
 | 
							if (!n)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							start = (loff_t)page->index * PAGE_SIZE; /* May regress with THPs */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_debug("wback %lx", page->index);
 | 
							_debug("wback %lx", page->index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/* At this point we hold neither the i_pages lock nor the
 | 
				
			||||||
		 * at this point we hold neither the i_pages lock nor the
 | 
					 | 
				
			||||||
		 * page lock: the page may be truncated or invalidated
 | 
							 * page lock: the page may be truncated or invalidated
 | 
				
			||||||
		 * (changing page->mapping to NULL), or even swizzled
 | 
							 * (changing page->mapping to NULL), or even swizzled
 | 
				
			||||||
		 * back from swapper_space to tmpfs file mapping
 | 
							 * back from swapper_space to tmpfs file mapping
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
 | 
							if (wbc->sync_mode != WB_SYNC_NONE) {
 | 
				
			||||||
			ret = lock_page_killable(page);
 | 
								ret = lock_page_killable(page);
 | 
				
			||||||
			if (ret < 0) {
 | 
								if (ret < 0) {
 | 
				
			||||||
				put_page(page);
 | 
									put_page(page);
 | 
				
			||||||
			_leave(" = %d", ret);
 | 
					 | 
				
			||||||
				return ret;
 | 
									return ret;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (!trylock_page(page)) {
 | 
				
			||||||
 | 
									put_page(page);
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (page->mapping != mapping || !PageDirty(page)) {
 | 
							if (page->mapping != mapping || !PageDirty(page)) {
 | 
				
			||||||
 | 
								start += thp_size(page);
 | 
				
			||||||
			unlock_page(page);
 | 
								unlock_page(page);
 | 
				
			||||||
			put_page(page);
 | 
								put_page(page);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					@ -761,20 +806,20 @@ static int afs_writepages_region(struct address_space *mapping,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!clear_page_dirty_for_io(page))
 | 
							if (!clear_page_dirty_for_io(page))
 | 
				
			||||||
			BUG();
 | 
								BUG();
 | 
				
			||||||
		ret = afs_write_back_from_locked_page(mapping, wbc, page, end);
 | 
							ret = afs_write_back_from_locked_page(mapping, wbc, page, start, end);
 | 
				
			||||||
		put_page(page);
 | 
							put_page(page);
 | 
				
			||||||
		if (ret < 0) {
 | 
							if (ret < 0) {
 | 
				
			||||||
			_leave(" = %d", ret);
 | 
								_leave(" = %zd", ret);
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		wbc->nr_to_write -= ret;
 | 
							start += ret * PAGE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cond_resched();
 | 
							cond_resched();
 | 
				
			||||||
	} while (index < end && wbc->nr_to_write > 0);
 | 
						} while (wbc->nr_to_write > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*_next = index;
 | 
						*_next = start;
 | 
				
			||||||
	_leave(" = 0 [%lx]", *_next);
 | 
						_leave(" = 0 [%llx]", *_next);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -785,7 +830,7 @@ int afs_writepages(struct address_space *mapping,
 | 
				
			||||||
		   struct writeback_control *wbc)
 | 
							   struct writeback_control *wbc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
						struct afs_vnode *vnode = AFS_FS_I(mapping->host);
 | 
				
			||||||
	pgoff_t start, end, next;
 | 
						loff_t start, next;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("");
 | 
						_enter("");
 | 
				
			||||||
| 
						 | 
					@ -800,22 +845,19 @@ int afs_writepages(struct address_space *mapping,
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (wbc->range_cyclic) {
 | 
						if (wbc->range_cyclic) {
 | 
				
			||||||
		start = mapping->writeback_index;
 | 
							start = mapping->writeback_index * PAGE_SIZE;
 | 
				
			||||||
		end = -1;
 | 
							ret = afs_writepages_region(mapping, wbc, start, LLONG_MAX, &next);
 | 
				
			||||||
		ret = afs_writepages_region(mapping, wbc, start, end, &next);
 | 
					 | 
				
			||||||
		if (start > 0 && wbc->nr_to_write > 0 && ret == 0)
 | 
							if (start > 0 && wbc->nr_to_write > 0 && ret == 0)
 | 
				
			||||||
			ret = afs_writepages_region(mapping, wbc, 0, start,
 | 
								ret = afs_writepages_region(mapping, wbc, 0, start,
 | 
				
			||||||
						    &next);
 | 
											    &next);
 | 
				
			||||||
		mapping->writeback_index = next;
 | 
							mapping->writeback_index = next / PAGE_SIZE;
 | 
				
			||||||
	} else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) {
 | 
						} else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) {
 | 
				
			||||||
		end = (pgoff_t)(LLONG_MAX >> PAGE_SHIFT);
 | 
							ret = afs_writepages_region(mapping, wbc, 0, LLONG_MAX, &next);
 | 
				
			||||||
		ret = afs_writepages_region(mapping, wbc, 0, end, &next);
 | 
					 | 
				
			||||||
		if (wbc->nr_to_write > 0)
 | 
							if (wbc->nr_to_write > 0)
 | 
				
			||||||
			mapping->writeback_index = next;
 | 
								mapping->writeback_index = next;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		start = wbc->range_start >> PAGE_SHIFT;
 | 
							ret = afs_writepages_region(mapping, wbc,
 | 
				
			||||||
		end = wbc->range_end >> PAGE_SHIFT;
 | 
										    wbc->range_start, wbc->range_end, &next);
 | 
				
			||||||
		ret = afs_writepages_region(mapping, wbc, start, end, &next);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	up_read(&vnode->validate_lock);
 | 
						up_read(&vnode->validate_lock);
 | 
				
			||||||
| 
						 | 
					@ -873,13 +915,13 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
 | 
					vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct page *page = thp_head(vmf->page);
 | 
				
			||||||
	struct file *file = vmf->vma->vm_file;
 | 
						struct file *file = vmf->vma->vm_file;
 | 
				
			||||||
	struct inode *inode = file_inode(file);
 | 
						struct inode *inode = file_inode(file);
 | 
				
			||||||
	struct afs_vnode *vnode = AFS_FS_I(inode);
 | 
						struct afs_vnode *vnode = AFS_FS_I(inode);
 | 
				
			||||||
	unsigned long priv;
 | 
						unsigned long priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_enter("{{%llx:%llu}},{%lx}",
 | 
						_enter("{{%llx:%llu}},{%lx}", vnode->fid.vid, vnode->fid.vnode, page->index);
 | 
				
			||||||
	       vnode->fid.vid, vnode->fid.vnode, vmf->page->index);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sb_start_pagefault(inode->i_sb);
 | 
						sb_start_pagefault(inode->i_sb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -887,30 +929,32 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
 | 
				
			||||||
	 * be modified.  We then assume the entire page will need writing back.
 | 
						 * be modified.  We then assume the entire page will need writing back.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
#ifdef CONFIG_AFS_FSCACHE
 | 
					#ifdef CONFIG_AFS_FSCACHE
 | 
				
			||||||
	if (PageFsCache(vmf->page) &&
 | 
						if (PageFsCache(page) &&
 | 
				
			||||||
	    wait_on_page_bit_killable(vmf->page, PG_fscache) < 0)
 | 
						    wait_on_page_bit_killable(page, PG_fscache) < 0)
 | 
				
			||||||
		return VM_FAULT_RETRY;
 | 
							return VM_FAULT_RETRY;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (wait_on_page_writeback_killable(vmf->page))
 | 
						if (wait_on_page_writeback_killable(page))
 | 
				
			||||||
		return VM_FAULT_RETRY;
 | 
							return VM_FAULT_RETRY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (lock_page_killable(vmf->page) < 0)
 | 
						if (lock_page_killable(page) < 0)
 | 
				
			||||||
		return VM_FAULT_RETRY;
 | 
							return VM_FAULT_RETRY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We mustn't change page->private until writeback is complete as that
 | 
						/* We mustn't change page->private until writeback is complete as that
 | 
				
			||||||
	 * details the portion of the page we need to write back and we might
 | 
						 * details the portion of the page we need to write back and we might
 | 
				
			||||||
	 * need to redirty the page if there's a problem.
 | 
						 * need to redirty the page if there's a problem.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	wait_on_page_writeback(vmf->page);
 | 
						wait_on_page_writeback(page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	priv = afs_page_dirty(vmf->page, 0, PAGE_SIZE);
 | 
						priv = afs_page_dirty(page, 0, thp_size(page));
 | 
				
			||||||
	priv = afs_page_dirty_mmapped(priv);
 | 
						priv = afs_page_dirty_mmapped(priv);
 | 
				
			||||||
	if (PagePrivate(vmf->page))
 | 
						if (PagePrivate(page)) {
 | 
				
			||||||
		set_page_private(vmf->page, priv);
 | 
							set_page_private(page, priv);
 | 
				
			||||||
	else
 | 
							trace_afs_page_dirty(vnode, tracepoint_string("mkwrite+"), page);
 | 
				
			||||||
		attach_page_private(vmf->page, (void *)priv);
 | 
						} else {
 | 
				
			||||||
	trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), vmf->page);
 | 
							attach_page_private(page, (void *)priv);
 | 
				
			||||||
 | 
							trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), page);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	file_update_time(file);
 | 
						file_update_time(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sb_end_pagefault(inode->i_sb);
 | 
						sb_end_pagefault(inode->i_sb);
 | 
				
			||||||
| 
						 | 
					@ -963,7 +1007,7 @@ int afs_launder_page(struct page *page)
 | 
				
			||||||
	priv = page_private(page);
 | 
						priv = page_private(page);
 | 
				
			||||||
	if (clear_page_dirty_for_io(page)) {
 | 
						if (clear_page_dirty_for_io(page)) {
 | 
				
			||||||
		f = 0;
 | 
							f = 0;
 | 
				
			||||||
		t = PAGE_SIZE;
 | 
							t = thp_size(page);
 | 
				
			||||||
		if (PagePrivate(page)) {
 | 
							if (PagePrivate(page)) {
 | 
				
			||||||
			f = afs_page_dirty_from(page, priv);
 | 
								f = afs_page_dirty_from(page, priv);
 | 
				
			||||||
			t = afs_page_dirty_to(page, priv);
 | 
								t = afs_page_dirty_to(page, priv);
 | 
				
			||||||
| 
						 | 
					@ -975,12 +1019,12 @@ int afs_launder_page(struct page *page)
 | 
				
			||||||
		iov_iter_bvec(&iter, WRITE, bv, 1, bv[0].bv_len);
 | 
							iov_iter_bvec(&iter, WRITE, bv, 1, bv[0].bv_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		trace_afs_page_dirty(vnode, tracepoint_string("launder"), page);
 | 
							trace_afs_page_dirty(vnode, tracepoint_string("launder"), page);
 | 
				
			||||||
		ret = afs_store_data(vnode, &iter, (loff_t)page->index << PAGE_SHIFT,
 | 
							ret = afs_store_data(vnode, &iter, (loff_t)page->index * PAGE_SIZE,
 | 
				
			||||||
				     page->index, page->index, true);
 | 
									     true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	detach_page_private(page);
 | 
					 | 
				
			||||||
	trace_afs_page_dirty(vnode, tracepoint_string("laundered"), page);
 | 
						trace_afs_page_dirty(vnode, tracepoint_string("laundered"), page);
 | 
				
			||||||
 | 
						detach_page_private(page);
 | 
				
			||||||
	wait_on_page_fscache(page);
 | 
						wait_on_page_fscache(page);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue