mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	netfs: Provide a writepages implementation
Provide an implementation of writepages for network filesystems to delegate to. Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> cc: linux-cachefs@redhat.com cc: linux-fsdevel@vger.kernel.org cc: linux-mm@kvack.org
This commit is contained in:
		
							parent
							
								
									e0ace6ca98
								
							
						
					
					
						commit
						62c3b7481b
					
				
					 2 changed files with 638 additions and 0 deletions
				
			
		| 
						 | 
					@ -32,6 +32,18 @@ static void netfs_set_group(struct folio *folio, struct netfs_group *netfs_group
 | 
				
			||||||
		folio_attach_private(folio, netfs_get_group(netfs_group));
 | 
							folio_attach_private(folio, netfs_get_group(netfs_group));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if IS_ENABLED(CONFIG_FSCACHE)
 | 
				
			||||||
 | 
					static void netfs_folio_start_fscache(bool caching, struct folio *folio)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (caching)
 | 
				
			||||||
 | 
							folio_start_fscache(folio);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					static void netfs_folio_start_fscache(bool caching, struct folio *folio)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Decide how we should modify a folio.  We might be attempting to do
 | 
					 * Decide how we should modify a folio.  We might be attempting to do
 | 
				
			||||||
 * write-streaming, in which case we don't want to a local RMW cycle if we can
 | 
					 * write-streaming, in which case we don't want to a local RMW cycle if we can
 | 
				
			||||||
| 
						 | 
					@ -475,3 +487,627 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(netfs_page_mkwrite);
 | 
					EXPORT_SYMBOL(netfs_page_mkwrite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Kill all the pages in the given range
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void netfs_kill_pages(struct address_space *mapping,
 | 
				
			||||||
 | 
								     loff_t start, loff_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct folio *folio;
 | 
				
			||||||
 | 
						pgoff_t index = start / PAGE_SIZE;
 | 
				
			||||||
 | 
						pgoff_t last = (start + len - 1) / PAGE_SIZE, next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%llx-%llx", start, start + len - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							_debug("kill %lx (to %lx)", index, last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							folio = filemap_get_folio(mapping, index);
 | 
				
			||||||
 | 
							if (IS_ERR(folio)) {
 | 
				
			||||||
 | 
								next = index + 1;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							next = folio_next_index(folio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							trace_netfs_folio(folio, netfs_folio_trace_kill);
 | 
				
			||||||
 | 
							folio_clear_uptodate(folio);
 | 
				
			||||||
 | 
							if (folio_test_fscache(folio))
 | 
				
			||||||
 | 
								folio_end_fscache(folio);
 | 
				
			||||||
 | 
							folio_end_writeback(folio);
 | 
				
			||||||
 | 
							folio_lock(folio);
 | 
				
			||||||
 | 
							generic_error_remove_page(mapping, &folio->page);
 | 
				
			||||||
 | 
							folio_unlock(folio);
 | 
				
			||||||
 | 
							folio_put(folio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} while (index = next, index <= last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Redirty all the pages in a given range.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void netfs_redirty_pages(struct address_space *mapping,
 | 
				
			||||||
 | 
									loff_t start, loff_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct folio *folio;
 | 
				
			||||||
 | 
						pgoff_t index = start / PAGE_SIZE;
 | 
				
			||||||
 | 
						pgoff_t last = (start + len - 1) / PAGE_SIZE, next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%llx-%llx", start, start + len - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							_debug("redirty %llx @%llx", len, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							folio = filemap_get_folio(mapping, index);
 | 
				
			||||||
 | 
							if (IS_ERR(folio)) {
 | 
				
			||||||
 | 
								next = index + 1;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							next = folio_next_index(folio);
 | 
				
			||||||
 | 
							trace_netfs_folio(folio, netfs_folio_trace_redirty);
 | 
				
			||||||
 | 
							filemap_dirty_folio(mapping, folio);
 | 
				
			||||||
 | 
							if (folio_test_fscache(folio))
 | 
				
			||||||
 | 
								folio_end_fscache(folio);
 | 
				
			||||||
 | 
							folio_end_writeback(folio);
 | 
				
			||||||
 | 
							folio_put(folio);
 | 
				
			||||||
 | 
						} while (index = next, index <= last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						balance_dirty_pages_ratelimited(mapping);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Completion of write to server
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void netfs_pages_written_back(struct netfs_io_request *wreq)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct address_space *mapping = wreq->mapping;
 | 
				
			||||||
 | 
						struct netfs_folio *finfo;
 | 
				
			||||||
 | 
						struct netfs_group *group = NULL;
 | 
				
			||||||
 | 
						struct folio *folio;
 | 
				
			||||||
 | 
						pgoff_t last;
 | 
				
			||||||
 | 
						int gcount = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						XA_STATE(xas, &mapping->i_pages, wreq->start / PAGE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%llx-%llx", wreq->start, wreq->start + wreq->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						last = (wreq->start + wreq->len - 1) / PAGE_SIZE;
 | 
				
			||||||
 | 
						xas_for_each(&xas, folio, last) {
 | 
				
			||||||
 | 
							WARN(!folio_test_writeback(folio),
 | 
				
			||||||
 | 
							     "bad %zx @%llx page %lx %lx\n",
 | 
				
			||||||
 | 
							     wreq->len, wreq->start, folio_index(folio), last);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((finfo = netfs_folio_info(folio))) {
 | 
				
			||||||
 | 
								/* Streaming writes cannot be redirtied whilst under
 | 
				
			||||||
 | 
								 * writeback, so discard the streaming record.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								folio_detach_private(folio);
 | 
				
			||||||
 | 
								group = finfo->netfs_group;
 | 
				
			||||||
 | 
								gcount++;
 | 
				
			||||||
 | 
								trace_netfs_folio(folio, netfs_folio_trace_clear_s);
 | 
				
			||||||
 | 
								kfree(finfo);
 | 
				
			||||||
 | 
							} else if ((group = netfs_folio_group(folio))) {
 | 
				
			||||||
 | 
								/* Need to detach the group pointer if the page didn't
 | 
				
			||||||
 | 
								 * get redirtied.  If it has been redirtied, then it
 | 
				
			||||||
 | 
								 * must be within the same group.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (folio_test_dirty(folio)) {
 | 
				
			||||||
 | 
									trace_netfs_folio(folio, netfs_folio_trace_redirtied);
 | 
				
			||||||
 | 
									goto end_wb;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (folio_trylock(folio)) {
 | 
				
			||||||
 | 
									if (!folio_test_dirty(folio)) {
 | 
				
			||||||
 | 
										folio_detach_private(folio);
 | 
				
			||||||
 | 
										gcount++;
 | 
				
			||||||
 | 
										trace_netfs_folio(folio, netfs_folio_trace_clear_g);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										trace_netfs_folio(folio, netfs_folio_trace_redirtied);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									folio_unlock(folio);
 | 
				
			||||||
 | 
									goto end_wb;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								xas_pause(&xas);
 | 
				
			||||||
 | 
								rcu_read_unlock();
 | 
				
			||||||
 | 
								folio_lock(folio);
 | 
				
			||||||
 | 
								if (!folio_test_dirty(folio)) {
 | 
				
			||||||
 | 
									folio_detach_private(folio);
 | 
				
			||||||
 | 
									gcount++;
 | 
				
			||||||
 | 
									trace_netfs_folio(folio, netfs_folio_trace_clear_g);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									trace_netfs_folio(folio, netfs_folio_trace_redirtied);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								folio_unlock(folio);
 | 
				
			||||||
 | 
								rcu_read_lock();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								trace_netfs_folio(folio, netfs_folio_trace_clear);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						end_wb:
 | 
				
			||||||
 | 
							if (folio_test_fscache(folio))
 | 
				
			||||||
 | 
								folio_end_fscache(folio);
 | 
				
			||||||
 | 
							folio_end_writeback(folio);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						netfs_put_group_many(group, gcount);
 | 
				
			||||||
 | 
						_leave("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Deal with the disposition of the folios that are under writeback to close
 | 
				
			||||||
 | 
					 * out the operation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void netfs_cleanup_buffered_write(struct netfs_io_request *wreq)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct address_space *mapping = wreq->mapping;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (wreq->error) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							netfs_pages_written_back(wreq);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_notice("R=%08x Unexpected error %d\n", wreq->debug_id, wreq->error);
 | 
				
			||||||
 | 
							fallthrough;
 | 
				
			||||||
 | 
						case -EACCES:
 | 
				
			||||||
 | 
						case -EPERM:
 | 
				
			||||||
 | 
						case -ENOKEY:
 | 
				
			||||||
 | 
						case -EKEYEXPIRED:
 | 
				
			||||||
 | 
						case -EKEYREJECTED:
 | 
				
			||||||
 | 
						case -EKEYREVOKED:
 | 
				
			||||||
 | 
						case -ENETRESET:
 | 
				
			||||||
 | 
						case -EDQUOT:
 | 
				
			||||||
 | 
						case -ENOSPC:
 | 
				
			||||||
 | 
							netfs_redirty_pages(mapping, wreq->start, wreq->len);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case -EROFS:
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						case -EREMOTEIO:
 | 
				
			||||||
 | 
						case -EFBIG:
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
						case -ENOMEDIUM:
 | 
				
			||||||
 | 
						case -ENXIO:
 | 
				
			||||||
 | 
							netfs_kill_pages(mapping, wreq->start, wreq->len);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (wreq->error)
 | 
				
			||||||
 | 
							mapping_set_error(mapping, wreq->error);
 | 
				
			||||||
 | 
						if (wreq->netfs_ops->done)
 | 
				
			||||||
 | 
							wreq->netfs_ops->done(wreq);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 netfs_extend_writeback(struct address_space *mapping,
 | 
				
			||||||
 | 
									   struct netfs_group *group,
 | 
				
			||||||
 | 
									   struct xa_state *xas,
 | 
				
			||||||
 | 
									   long *_count,
 | 
				
			||||||
 | 
									   loff_t start,
 | 
				
			||||||
 | 
									   loff_t max_len,
 | 
				
			||||||
 | 
									   bool caching,
 | 
				
			||||||
 | 
									   size_t *_len,
 | 
				
			||||||
 | 
									   size_t *_top)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct netfs_folio *finfo;
 | 
				
			||||||
 | 
						struct folio_batch fbatch;
 | 
				
			||||||
 | 
						struct folio *folio;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						pgoff_t index = (start + *_len) / PAGE_SIZE;
 | 
				
			||||||
 | 
						size_t len;
 | 
				
			||||||
 | 
						void *priv;
 | 
				
			||||||
 | 
						bool stop = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						folio_batch_init(&fbatch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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, folio, ULONG_MAX) {
 | 
				
			||||||
 | 
								stop = true;
 | 
				
			||||||
 | 
								if (xas_retry(xas, folio))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								if (xa_is_value(folio))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								if (folio_index(folio) != index) {
 | 
				
			||||||
 | 
									xas_reset(xas);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!folio_try_get_rcu(folio)) {
 | 
				
			||||||
 | 
									xas_reset(xas);
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Has the folio moved or been split? */
 | 
				
			||||||
 | 
								if (unlikely(folio != xas_reload(xas))) {
 | 
				
			||||||
 | 
									folio_put(folio);
 | 
				
			||||||
 | 
									xas_reset(xas);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!folio_trylock(folio)) {
 | 
				
			||||||
 | 
									folio_put(folio);
 | 
				
			||||||
 | 
									xas_reset(xas);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (!folio_test_dirty(folio) ||
 | 
				
			||||||
 | 
								    folio_test_writeback(folio) ||
 | 
				
			||||||
 | 
								    folio_test_fscache(folio)) {
 | 
				
			||||||
 | 
									folio_unlock(folio);
 | 
				
			||||||
 | 
									folio_put(folio);
 | 
				
			||||||
 | 
									xas_reset(xas);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stop = false;
 | 
				
			||||||
 | 
								len = folio_size(folio);
 | 
				
			||||||
 | 
								priv = folio_get_private(folio);
 | 
				
			||||||
 | 
								if ((const struct netfs_group *)priv != group) {
 | 
				
			||||||
 | 
									stop = true;
 | 
				
			||||||
 | 
									finfo = netfs_folio_info(folio);
 | 
				
			||||||
 | 
									if (finfo->netfs_group != group ||
 | 
				
			||||||
 | 
									    finfo->dirty_offset > 0) {
 | 
				
			||||||
 | 
										folio_unlock(folio);
 | 
				
			||||||
 | 
										folio_put(folio);
 | 
				
			||||||
 | 
										xas_reset(xas);
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									len = finfo->dirty_len;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								*_top += folio_size(folio);
 | 
				
			||||||
 | 
								index += folio_nr_pages(folio);
 | 
				
			||||||
 | 
								*_count -= folio_nr_pages(folio);
 | 
				
			||||||
 | 
								*_len += len;
 | 
				
			||||||
 | 
								if (*_len >= max_len || *_count <= 0)
 | 
				
			||||||
 | 
									stop = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!folio_batch_add(&fbatch, folio))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								if (stop)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							xas_pause(xas);
 | 
				
			||||||
 | 
							rcu_read_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Now, if we obtained any folios, we can shift them to being
 | 
				
			||||||
 | 
							 * writable and mark them for caching.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!folio_batch_count(&fbatch))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < folio_batch_count(&fbatch); i++) {
 | 
				
			||||||
 | 
								folio = fbatch.folios[i];
 | 
				
			||||||
 | 
								trace_netfs_folio(folio, netfs_folio_trace_store_plus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!folio_clear_dirty_for_io(folio))
 | 
				
			||||||
 | 
									BUG();
 | 
				
			||||||
 | 
								folio_start_writeback(folio);
 | 
				
			||||||
 | 
								netfs_folio_start_fscache(caching, folio);
 | 
				
			||||||
 | 
								folio_unlock(folio);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							folio_batch_release(&fbatch);
 | 
				
			||||||
 | 
							cond_resched();
 | 
				
			||||||
 | 
						} while (!stop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Synchronously write back the locked page and any subsequent non-locked dirty
 | 
				
			||||||
 | 
					 * pages.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static ssize_t netfs_write_back_from_locked_folio(struct address_space *mapping,
 | 
				
			||||||
 | 
											  struct writeback_control *wbc,
 | 
				
			||||||
 | 
											  struct netfs_group *group,
 | 
				
			||||||
 | 
											  struct xa_state *xas,
 | 
				
			||||||
 | 
											  struct folio *folio,
 | 
				
			||||||
 | 
											  unsigned long long start,
 | 
				
			||||||
 | 
											  unsigned long long end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct netfs_io_request *wreq;
 | 
				
			||||||
 | 
						struct netfs_folio *finfo;
 | 
				
			||||||
 | 
						struct netfs_inode *ctx = netfs_inode(mapping->host);
 | 
				
			||||||
 | 
						unsigned long long i_size = i_size_read(&ctx->inode);
 | 
				
			||||||
 | 
						size_t len, max_len;
 | 
				
			||||||
 | 
						bool caching = netfs_is_cache_enabled(ctx);
 | 
				
			||||||
 | 
						long count = wbc->nr_to_write;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter(",%lx,%llx-%llx,%u", folio_index(folio), start, end, caching);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wreq = netfs_alloc_request(mapping, NULL, start, folio_size(folio),
 | 
				
			||||||
 | 
									   NETFS_WRITEBACK);
 | 
				
			||||||
 | 
						if (IS_ERR(wreq)) {
 | 
				
			||||||
 | 
							folio_unlock(folio);
 | 
				
			||||||
 | 
							return PTR_ERR(wreq);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!folio_clear_dirty_for_io(folio))
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						folio_start_writeback(folio);
 | 
				
			||||||
 | 
						netfs_folio_start_fscache(caching, folio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						count -= folio_nr_pages(folio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find all consecutive lockable dirty pages that have contiguous
 | 
				
			||||||
 | 
						 * written regions, stopping when we find a page that is not
 | 
				
			||||||
 | 
						 * immediately lockable, is not dirty or is missing, or we reach the
 | 
				
			||||||
 | 
						 * end of the range.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						trace_netfs_folio(folio, netfs_folio_trace_store);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						len = wreq->len;
 | 
				
			||||||
 | 
						finfo = netfs_folio_info(folio);
 | 
				
			||||||
 | 
						if (finfo) {
 | 
				
			||||||
 | 
							start += finfo->dirty_offset;
 | 
				
			||||||
 | 
							if (finfo->dirty_offset + finfo->dirty_len != len) {
 | 
				
			||||||
 | 
								len = finfo->dirty_len;
 | 
				
			||||||
 | 
								goto cant_expand;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							len = finfo->dirty_len;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 (len < max_len)
 | 
				
			||||||
 | 
								netfs_extend_writeback(mapping, group, xas, &count, start,
 | 
				
			||||||
 | 
										       max_len, caching, &len, &wreq->upper_len);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cant_expand:
 | 
				
			||||||
 | 
						len = min_t(unsigned long long, len, i_size - start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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
 | 
				
			||||||
 | 
						 * have been unlocked.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						folio_unlock(folio);
 | 
				
			||||||
 | 
						wreq->start = start;
 | 
				
			||||||
 | 
						wreq->len = len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (start < i_size) {
 | 
				
			||||||
 | 
							_debug("write back %zx @%llx [%llx]", len, start, i_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Speculatively write to the cache.  We have to fix this up
 | 
				
			||||||
 | 
							 * later if the store fails.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							wreq->cleanup = netfs_cleanup_buffered_write;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							iov_iter_xarray(&wreq->iter, ITER_SOURCE, &mapping->i_pages, start,
 | 
				
			||||||
 | 
									wreq->upper_len);
 | 
				
			||||||
 | 
							__set_bit(NETFS_RREQ_UPLOAD_TO_SERVER, &wreq->flags);
 | 
				
			||||||
 | 
							ret = netfs_begin_write(wreq, true, netfs_write_trace_writeback);
 | 
				
			||||||
 | 
							if (ret == 0 || ret == -EIOCBQUEUED)
 | 
				
			||||||
 | 
								wbc->nr_to_write -= len / PAGE_SIZE;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							_debug("write discard %zx @%llx [%llx]", len, start, i_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* The dirty region was entirely beyond the EOF. */
 | 
				
			||||||
 | 
							fscache_clear_page_bits(mapping, start, len, caching);
 | 
				
			||||||
 | 
							netfs_pages_written_back(wreq);
 | 
				
			||||||
 | 
							ret = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						netfs_put_request(wreq, false, netfs_rreq_trace_put_return);
 | 
				
			||||||
 | 
						_leave(" = 1");
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Write a region of pages back to the server
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static ssize_t netfs_writepages_begin(struct address_space *mapping,
 | 
				
			||||||
 | 
									      struct writeback_control *wbc,
 | 
				
			||||||
 | 
									      struct netfs_group *group,
 | 
				
			||||||
 | 
									      struct xa_state *xas,
 | 
				
			||||||
 | 
									      unsigned long long *_start,
 | 
				
			||||||
 | 
									      unsigned long long end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct netfs_folio *finfo;
 | 
				
			||||||
 | 
						struct folio *folio;
 | 
				
			||||||
 | 
						unsigned long long start = *_start;
 | 
				
			||||||
 | 
						ssize_t ret;
 | 
				
			||||||
 | 
						void *priv;
 | 
				
			||||||
 | 
						int skips = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("%llx,%llx,", start, end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					search_again:
 | 
				
			||||||
 | 
						/* Find the first dirty page in the group. */
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (;;) {
 | 
				
			||||||
 | 
							folio = xas_find_marked(xas, end / PAGE_SIZE, PAGECACHE_TAG_DIRTY);
 | 
				
			||||||
 | 
							if (xas_retry(xas, folio) || xa_is_value(folio))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if (!folio)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!folio_try_get_rcu(folio)) {
 | 
				
			||||||
 | 
								xas_reset(xas);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (unlikely(folio != xas_reload(xas))) {
 | 
				
			||||||
 | 
								folio_put(folio);
 | 
				
			||||||
 | 
								xas_reset(xas);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Skip any dirty folio that's not in the group of interest. */
 | 
				
			||||||
 | 
							priv = folio_get_private(folio);
 | 
				
			||||||
 | 
							if ((const struct netfs_group *)priv != group) {
 | 
				
			||||||
 | 
								finfo = netfs_folio_info(folio);
 | 
				
			||||||
 | 
								if (finfo->netfs_group != group) {
 | 
				
			||||||
 | 
									folio_put(folio);
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							xas_pause(xas);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						if (!folio)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start = folio_pos(folio); /* May regress with THPs */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_debug("wback %lx", folio_index(folio));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* At this point we hold neither the i_pages lock nor the page lock:
 | 
				
			||||||
 | 
						 * the page may be truncated or invalidated (changing page->mapping to
 | 
				
			||||||
 | 
						 * NULL), or even swizzled back from swapper_space to tmpfs file
 | 
				
			||||||
 | 
						 * mapping
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					lock_again:
 | 
				
			||||||
 | 
						if (wbc->sync_mode != WB_SYNC_NONE) {
 | 
				
			||||||
 | 
							ret = folio_lock_killable(folio);
 | 
				
			||||||
 | 
							if (ret < 0)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (!folio_trylock(folio))
 | 
				
			||||||
 | 
								goto search_again;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (folio->mapping != mapping ||
 | 
				
			||||||
 | 
						    !folio_test_dirty(folio)) {
 | 
				
			||||||
 | 
							start += folio_size(folio);
 | 
				
			||||||
 | 
							folio_unlock(folio);
 | 
				
			||||||
 | 
							goto search_again;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (folio_test_writeback(folio) ||
 | 
				
			||||||
 | 
						    folio_test_fscache(folio)) {
 | 
				
			||||||
 | 
							folio_unlock(folio);
 | 
				
			||||||
 | 
							if (wbc->sync_mode != WB_SYNC_NONE) {
 | 
				
			||||||
 | 
								folio_wait_writeback(folio);
 | 
				
			||||||
 | 
					#ifdef CONFIG_NETFS_FSCACHE
 | 
				
			||||||
 | 
								folio_wait_fscache(folio);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								goto lock_again;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							start += folio_size(folio);
 | 
				
			||||||
 | 
							if (wbc->sync_mode == WB_SYNC_NONE) {
 | 
				
			||||||
 | 
								if (skips >= 5 || need_resched()) {
 | 
				
			||||||
 | 
									ret = 0;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								skips++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							goto search_again;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = netfs_write_back_from_locked_folio(mapping, wbc, group, xas,
 | 
				
			||||||
 | 
											 folio, start, end);
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						if (ret > 0)
 | 
				
			||||||
 | 
							*_start = start + ret;
 | 
				
			||||||
 | 
						_leave(" = %zd [%llx]", ret, *_start);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Write a region of pages back to the server
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int netfs_writepages_region(struct address_space *mapping,
 | 
				
			||||||
 | 
									   struct writeback_control *wbc,
 | 
				
			||||||
 | 
									   struct netfs_group *group,
 | 
				
			||||||
 | 
									   unsigned long long *_start,
 | 
				
			||||||
 | 
									   unsigned long long end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ssize_t ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						XA_STATE(xas, &mapping->i_pages, *_start / PAGE_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							ret = netfs_writepages_begin(mapping, wbc, group, &xas,
 | 
				
			||||||
 | 
										     _start, end);
 | 
				
			||||||
 | 
							if (ret > 0 && wbc->nr_to_write > 0)
 | 
				
			||||||
 | 
								cond_resched();
 | 
				
			||||||
 | 
						} while (ret > 0 && wbc->nr_to_write > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret > 0 ? 0 : ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * write some of the pending data back to the server
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int netfs_writepages(struct address_space *mapping,
 | 
				
			||||||
 | 
							     struct writeback_control *wbc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct netfs_group *group = NULL;
 | 
				
			||||||
 | 
						loff_t start, end;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_enter("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We have to be careful as we can end up racing with setattr()
 | 
				
			||||||
 | 
						 * truncating the pagecache since the caller doesn't take a lock here
 | 
				
			||||||
 | 
						 * to prevent it.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (wbc->range_cyclic && mapping->writeback_index) {
 | 
				
			||||||
 | 
							start = mapping->writeback_index * PAGE_SIZE;
 | 
				
			||||||
 | 
							ret = netfs_writepages_region(mapping, wbc, group,
 | 
				
			||||||
 | 
										      &start, LLONG_MAX);
 | 
				
			||||||
 | 
							if (ret < 0)
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (wbc->nr_to_write <= 0) {
 | 
				
			||||||
 | 
								mapping->writeback_index = start / PAGE_SIZE;
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							start = 0;
 | 
				
			||||||
 | 
							end = mapping->writeback_index * PAGE_SIZE;
 | 
				
			||||||
 | 
							mapping->writeback_index = 0;
 | 
				
			||||||
 | 
							ret = netfs_writepages_region(mapping, wbc, group, &start, end);
 | 
				
			||||||
 | 
							if (ret == 0)
 | 
				
			||||||
 | 
								mapping->writeback_index = start / PAGE_SIZE;
 | 
				
			||||||
 | 
						} else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) {
 | 
				
			||||||
 | 
							start = 0;
 | 
				
			||||||
 | 
							ret = netfs_writepages_region(mapping, wbc, group,
 | 
				
			||||||
 | 
										      &start, LLONG_MAX);
 | 
				
			||||||
 | 
							if (wbc->nr_to_write > 0 && ret == 0)
 | 
				
			||||||
 | 
								mapping->writeback_index = start / PAGE_SIZE;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							start = wbc->range_start;
 | 
				
			||||||
 | 
							ret = netfs_writepages_region(mapping, wbc, group,
 | 
				
			||||||
 | 
										      &start, wbc->range_end);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						_leave(" = %d", ret);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(netfs_writepages);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -397,6 +397,8 @@ int netfs_read_folio(struct file *, struct folio *);
 | 
				
			||||||
int netfs_write_begin(struct netfs_inode *, struct file *,
 | 
					int netfs_write_begin(struct netfs_inode *, struct file *,
 | 
				
			||||||
		      struct address_space *, loff_t pos, unsigned int len,
 | 
							      struct address_space *, loff_t pos, unsigned int len,
 | 
				
			||||||
		      struct folio **, void **fsdata);
 | 
							      struct folio **, void **fsdata);
 | 
				
			||||||
 | 
					int netfs_writepages(struct address_space *mapping,
 | 
				
			||||||
 | 
							     struct writeback_control *wbc);
 | 
				
			||||||
bool netfs_dirty_folio(struct address_space *mapping, struct folio *folio);
 | 
					bool netfs_dirty_folio(struct address_space *mapping, struct folio *folio);
 | 
				
			||||||
int netfs_unpin_writeback(struct inode *inode, struct writeback_control *wbc);
 | 
					int netfs_unpin_writeback(struct inode *inode, struct writeback_control *wbc);
 | 
				
			||||||
void netfs_clear_inode_writeback(struct inode *inode, const void *aux);
 | 
					void netfs_clear_inode_writeback(struct inode *inode, const void *aux);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue