forked from mirrors/linux
		
	fs: Add aops->dirty_folio
This replaces ->set_page_dirty(). It returns a bool instead of an int and takes the address_space as a parameter instead of expecting the implementations to retrieve the address_space from the page. This is particularly important for filesystems which use FS_OPS for swap. Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org> Tested-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> Acked-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> Tested-by: Mike Marshall <hubcap@omnibond.com> # orangefs Tested-by: David Howells <dhowells@redhat.com> # afs
This commit is contained in:
		
							parent
							
								
									072acba6d0
								
							
						
					
					
						commit
						6f31a5a261
					
				
					 5 changed files with 31 additions and 23 deletions
				
			
		| 
						 | 
					@ -239,7 +239,7 @@ prototypes::
 | 
				
			||||||
	int (*writepage)(struct page *page, struct writeback_control *wbc);
 | 
						int (*writepage)(struct page *page, struct writeback_control *wbc);
 | 
				
			||||||
	int (*readpage)(struct file *, struct page *);
 | 
						int (*readpage)(struct file *, struct page *);
 | 
				
			||||||
	int (*writepages)(struct address_space *, struct writeback_control *);
 | 
						int (*writepages)(struct address_space *, struct writeback_control *);
 | 
				
			||||||
	int (*set_page_dirty)(struct page *page);
 | 
						bool (*dirty_folio)(struct address_space *, struct folio *folio);
 | 
				
			||||||
	void (*readahead)(struct readahead_control *);
 | 
						void (*readahead)(struct readahead_control *);
 | 
				
			||||||
	int (*readpages)(struct file *filp, struct address_space *mapping,
 | 
						int (*readpages)(struct file *filp, struct address_space *mapping,
 | 
				
			||||||
			struct list_head *pages, unsigned nr_pages);
 | 
								struct list_head *pages, unsigned nr_pages);
 | 
				
			||||||
| 
						 | 
					@ -264,7 +264,7 @@ prototypes::
 | 
				
			||||||
	int (*swap_deactivate)(struct file *);
 | 
						int (*swap_deactivate)(struct file *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
locking rules:
 | 
					locking rules:
 | 
				
			||||||
	All except set_page_dirty and freepage may block
 | 
						All except dirty_folio and freepage may block
 | 
				
			||||||
 | 
					
 | 
				
			||||||
======================	======================== =========	===============
 | 
					======================	======================== =========	===============
 | 
				
			||||||
ops			PageLocked(page)	 i_rwsem	invalidate_lock
 | 
					ops			PageLocked(page)	 i_rwsem	invalidate_lock
 | 
				
			||||||
| 
						 | 
					@ -272,7 +272,7 @@ ops			PageLocked(page)	 i_rwsem	invalidate_lock
 | 
				
			||||||
writepage:		yes, unlocks (see below)
 | 
					writepage:		yes, unlocks (see below)
 | 
				
			||||||
readpage:		yes, unlocks				shared
 | 
					readpage:		yes, unlocks				shared
 | 
				
			||||||
writepages:
 | 
					writepages:
 | 
				
			||||||
set_page_dirty		no
 | 
					dirty_folio		maybe
 | 
				
			||||||
readahead:		yes, unlocks				shared
 | 
					readahead:		yes, unlocks				shared
 | 
				
			||||||
readpages:		no					shared
 | 
					readpages:		no					shared
 | 
				
			||||||
write_begin:		locks the page		 exclusive
 | 
					write_begin:		locks the page		 exclusive
 | 
				
			||||||
| 
						 | 
					@ -361,10 +361,11 @@ If nr_to_write is NULL, all dirty pages must be written.
 | 
				
			||||||
writepages should _only_ write pages which are present on
 | 
					writepages should _only_ write pages which are present on
 | 
				
			||||||
mapping->io_pages.
 | 
					mapping->io_pages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
->set_page_dirty() is called from various places in the kernel
 | 
					->dirty_folio() is called from various places in the kernel when
 | 
				
			||||||
when the target page is marked as needing writeback.  It may be called
 | 
					the target folio is marked as needing writeback.  The folio cannot be
 | 
				
			||||||
under spinlock (it cannot block) and is sometimes called with the page
 | 
					truncated because either the caller holds the folio lock, or the caller
 | 
				
			||||||
not locked.
 | 
					has found the folio while holding the page table lock which will block
 | 
				
			||||||
 | 
					truncation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
->bmap() is currently used by legacy ioctl() (FIBMAP) provided by some
 | 
					->bmap() is currently used by legacy ioctl() (FIBMAP) provided by some
 | 
				
			||||||
filesystems and by the swapper. The latter will eventually go away.  Please,
 | 
					filesystems and by the swapper. The latter will eventually go away.  Please,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -658,7 +658,7 @@ pages, however the address_space has finer control of write sizes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The read process essentially only requires 'readpage'.  The write
 | 
					The read process essentially only requires 'readpage'.  The write
 | 
				
			||||||
process is more complicated and uses write_begin/write_end or
 | 
					process is more complicated and uses write_begin/write_end or
 | 
				
			||||||
set_page_dirty to write data into the address_space, and writepage and
 | 
					dirty_folio to write data into the address_space, and writepage and
 | 
				
			||||||
writepages to writeback data to storage.
 | 
					writepages to writeback data to storage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Adding and removing pages to/from an address_space is protected by the
 | 
					Adding and removing pages to/from an address_space is protected by the
 | 
				
			||||||
| 
						 | 
					@ -724,7 +724,7 @@ cache in your filesystem.  The following members are defined:
 | 
				
			||||||
		int (*writepage)(struct page *page, struct writeback_control *wbc);
 | 
							int (*writepage)(struct page *page, struct writeback_control *wbc);
 | 
				
			||||||
		int (*readpage)(struct file *, struct page *);
 | 
							int (*readpage)(struct file *, struct page *);
 | 
				
			||||||
		int (*writepages)(struct address_space *, struct writeback_control *);
 | 
							int (*writepages)(struct address_space *, struct writeback_control *);
 | 
				
			||||||
		int (*set_page_dirty)(struct page *page);
 | 
							bool (*dirty_folio)(struct address_space *, struct folio *);
 | 
				
			||||||
		void (*readahead)(struct readahead_control *);
 | 
							void (*readahead)(struct readahead_control *);
 | 
				
			||||||
		int (*readpages)(struct file *filp, struct address_space *mapping,
 | 
							int (*readpages)(struct file *filp, struct address_space *mapping,
 | 
				
			||||||
				 struct list_head *pages, unsigned nr_pages);
 | 
									 struct list_head *pages, unsigned nr_pages);
 | 
				
			||||||
| 
						 | 
					@ -793,13 +793,13 @@ cache in your filesystem.  The following members are defined:
 | 
				
			||||||
	This will choose pages from the address space that are tagged as
 | 
						This will choose pages from the address space that are tagged as
 | 
				
			||||||
	DIRTY and will pass them to ->writepage.
 | 
						DIRTY and will pass them to ->writepage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
``set_page_dirty``
 | 
					``dirty_folio``
 | 
				
			||||||
	called by the VM to set a page dirty.  This is particularly
 | 
						called by the VM to mark a folio as dirty.  This is particularly
 | 
				
			||||||
	needed if an address space attaches private data to a page, and
 | 
						needed if an address space attaches private data to a folio, and
 | 
				
			||||||
	that data needs to be updated when a page is dirtied.  This is
 | 
						that data needs to be updated when a folio is dirtied.  This is
 | 
				
			||||||
	called, for example, when a memory mapped page gets modified.
 | 
						called, for example, when a memory mapped page gets modified.
 | 
				
			||||||
	If defined, it should set the PageDirty flag, and the
 | 
						If defined, it should set the folio dirty flag, and the
 | 
				
			||||||
	PAGECACHE_TAG_DIRTY tag in the radix tree.
 | 
						PAGECACHE_TAG_DIRTY search mark in i_pages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
``readahead``
 | 
					``readahead``
 | 
				
			||||||
	Called by the VM to read pages associated with the address_space
 | 
						Called by the VM to read pages associated with the address_space
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -369,6 +369,7 @@ struct address_space_operations {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set a page dirty.  Return true if this dirtied it */
 | 
						/* Set a page dirty.  Return true if this dirtied it */
 | 
				
			||||||
	int (*set_page_dirty)(struct page *page);
 | 
						int (*set_page_dirty)(struct page *page);
 | 
				
			||||||
 | 
						bool (*dirty_folio)(struct address_space *, struct folio *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Reads in the requested pages. Unlike ->readpage(), this is
 | 
						 * Reads in the requested pages. Unlike ->readpage(), this is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2616,7 +2616,7 @@ EXPORT_SYMBOL(folio_redirty_for_writepage);
 | 
				
			||||||
 * folio_mark_dirty - Mark a folio as being modified.
 | 
					 * folio_mark_dirty - Mark a folio as being modified.
 | 
				
			||||||
 * @folio: The folio.
 | 
					 * @folio: The folio.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * For folios with a mapping this should be done under the page lock
 | 
					 * For folios with a mapping this should be done with the folio lock held
 | 
				
			||||||
 * for the benefit of asynchronous memory errors who prefer a consistent
 | 
					 * for the benefit of asynchronous memory errors who prefer a consistent
 | 
				
			||||||
 * dirty state. This rule can be broken in some special cases,
 | 
					 * dirty state. This rule can be broken in some special cases,
 | 
				
			||||||
 * but should be better not to.
 | 
					 * but should be better not to.
 | 
				
			||||||
| 
						 | 
					@ -2630,16 +2630,19 @@ bool folio_mark_dirty(struct folio *folio)
 | 
				
			||||||
	if (likely(mapping)) {
 | 
						if (likely(mapping)) {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * readahead/lru_deactivate_page could remain
 | 
							 * readahead/lru_deactivate_page could remain
 | 
				
			||||||
		 * PG_readahead/PG_reclaim due to race with end_page_writeback
 | 
							 * PG_readahead/PG_reclaim due to race with folio_end_writeback
 | 
				
			||||||
		 * About readahead, if the page is written, the flags would be
 | 
							 * About readahead, if the folio is written, the flags would be
 | 
				
			||||||
		 * reset. So no problem.
 | 
							 * reset. So no problem.
 | 
				
			||||||
		 * About lru_deactivate_page, if the page is redirty, the flag
 | 
							 * About lru_deactivate_page, if the folio is redirtied,
 | 
				
			||||||
		 * will be reset. So no problem. but if the page is used by readahead
 | 
							 * the flag will be reset. So no problem. but if the
 | 
				
			||||||
		 * it will confuse readahead and make it restart the size rampup
 | 
							 * folio is used by readahead it will confuse readahead
 | 
				
			||||||
		 * process. But it's a trivial problem.
 | 
							 * and make it restart the size rampup process. But it's
 | 
				
			||||||
 | 
							 * a trivial problem.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (folio_test_reclaim(folio))
 | 
							if (folio_test_reclaim(folio))
 | 
				
			||||||
			folio_clear_reclaim(folio);
 | 
								folio_clear_reclaim(folio);
 | 
				
			||||||
 | 
							if (mapping->a_ops->dirty_folio)
 | 
				
			||||||
 | 
								return mapping->a_ops->dirty_folio(mapping, folio);
 | 
				
			||||||
		return mapping->a_ops->set_page_dirty(&folio->page);
 | 
							return mapping->a_ops->set_page_dirty(&folio->page);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!folio_test_dirty(folio)) {
 | 
						if (!folio_test_dirty(folio)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -444,9 +444,12 @@ int swap_set_page_dirty(struct page *page)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (data_race(sis->flags & SWP_FS_OPS)) {
 | 
						if (data_race(sis->flags & SWP_FS_OPS)) {
 | 
				
			||||||
		struct address_space *mapping = sis->swap_file->f_mapping;
 | 
							struct address_space *mapping = sis->swap_file->f_mapping;
 | 
				
			||||||
 | 
							const struct address_space_operations *aops = mapping->a_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		VM_BUG_ON_PAGE(!PageSwapCache(page), page);
 | 
							VM_BUG_ON_PAGE(!PageSwapCache(page), page);
 | 
				
			||||||
		return mapping->a_ops->set_page_dirty(page);
 | 
							if (aops->dirty_folio)
 | 
				
			||||||
 | 
								return aops->dirty_folio(mapping, page_folio(page));
 | 
				
			||||||
 | 
							return aops->set_page_dirty(page);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		return __set_page_dirty_no_writeback(page);
 | 
							return __set_page_dirty_no_writeback(page);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue