diff mbox series

[1/3] mm: provide filemap_range_needs_writeback() helper

Message ID 20210208221829.17247-2-axboe@kernel.dk (mailing list archive)
State New, archived
Headers show
Series Improve IOCB_NOWAIT O_DIRECT | expand

Commit Message

Jens Axboe Feb. 8, 2021, 10:18 p.m. UTC
For O_DIRECT reads/writes, we check if we need to issue a call to
filemap_write_and_wait_range() to issue and/or wait for writeback for any
page in the given range. The existing mechanism just checks for a page in
the range, which is suboptimal for IOCB_NOWAIT as we'll fallback to the
slow path (and needing retry) if there's just a clean page cache page in
the range.

Provide filemap_range_needs_writeback() which tries a little harder to
check if we actually need to issue and/or wait for writeback in the
range.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 include/linux/fs.h |  2 ++
 mm/filemap.c       | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

Comments

Matthew Wilcox (Oracle) Feb. 8, 2021, 11:02 p.m. UTC | #1
On Mon, Feb 08, 2021 at 03:18:27PM -0700, Jens Axboe wrote:
> +	rcu_read_lock();
> +	xas_for_each(&xas, head, max) {
> +		if (xas_retry(&xas, head))
> +			continue;
> +		if (xa_is_value(head))
> +			continue;
> +		page = find_subpage(head, xas.xa_index);
> +		if (PageDirty(page) || PageLocked(page) || PageWriteback(page))
> +			break;
> +		page = NULL;
> +	}
> +	rcu_read_unlock();

There's no need to find the sub-page for any of these three conditions --
the bit will be set on the head page.
Jens Axboe Feb. 8, 2021, 11:21 p.m. UTC | #2
On 2/8/21 4:02 PM, Matthew Wilcox wrote:
> On Mon, Feb 08, 2021 at 03:18:27PM -0700, Jens Axboe wrote:
>> +	rcu_read_lock();
>> +	xas_for_each(&xas, head, max) {
>> +		if (xas_retry(&xas, head))
>> +			continue;
>> +		if (xa_is_value(head))
>> +			continue;
>> +		page = find_subpage(head, xas.xa_index);
>> +		if (PageDirty(page) || PageLocked(page) || PageWriteback(page))
>> +			break;
>> +		page = NULL;
>> +	}
>> +	rcu_read_unlock();
> 
> There's no need to find the sub-page for any of these three conditions --
> the bit will be set on the head page.

Gotcha, that makes it simpler. I'll make the edit.
diff mbox series

Patch

diff --git a/include/linux/fs.h b/include/linux/fs.h
index fd47deea7c17..def89222dfe9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2633,6 +2633,8 @@  static inline int filemap_fdatawait(struct address_space *mapping)
 
 extern bool filemap_range_has_page(struct address_space *, loff_t lstart,
 				  loff_t lend);
+extern bool filemap_range_needs_writeback(struct address_space *,
+					  loff_t lstart, loff_t lend);
 extern int filemap_write_and_wait_range(struct address_space *mapping,
 				        loff_t lstart, loff_t lend);
 extern int __filemap_fdatawrite_range(struct address_space *mapping,
diff --git a/mm/filemap.c b/mm/filemap.c
index aa0e0fb04670..1ed7acac8a1b 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -633,6 +633,52 @@  static bool mapping_needs_writeback(struct address_space *mapping)
 	return mapping->nrpages;
 }
 
+/**
+ * filemap_range_needs_writeback - check if range potentially needs writeback
+ * @mapping:           address space within which to check
+ * @start_byte:        offset in bytes where the range starts
+ * @end_byte:          offset in bytes where the range ends (inclusive)
+ *
+ * Find at least one page in the range supplied, usually used to check if
+ * direct writing in this range will trigger a writeback. Used by O_DIRECT
+ * read/write with IOCB_NOWAIT, to see if the caller needs to do
+ * filemap_write_and_wait_range() before proceeding.
+ *
+ * Return: %true if the caller should do filemap_write_and_wait_range() before
+ * doing O_DIRECT to a page in this range, %false otherwise.
+ */
+bool filemap_range_needs_writeback(struct address_space *mapping,
+				   loff_t start_byte, loff_t end_byte)
+{
+	struct page *page = NULL, *head;
+	XA_STATE(xas, &mapping->i_pages, start_byte >> PAGE_SHIFT);
+	pgoff_t max = end_byte >> PAGE_SHIFT;
+
+	if (!mapping_needs_writeback(mapping))
+		return false;
+	if (!mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) &&
+	    !mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK))
+		return false;
+	if (end_byte < start_byte)
+		return false;
+
+	rcu_read_lock();
+	xas_for_each(&xas, head, max) {
+		if (xas_retry(&xas, head))
+			continue;
+		if (xa_is_value(head))
+			continue;
+		page = find_subpage(head, xas.xa_index);
+		if (PageDirty(page) || PageLocked(page) || PageWriteback(page))
+			break;
+		page = NULL;
+	}
+	rcu_read_unlock();
+
+	return page != NULL;
+}
+EXPORT_SYMBOL_GPL(filemap_range_needs_writeback);
+
 /**
  * filemap_write_and_wait_range - write out & wait on a file range
  * @mapping:	the address_space for the pages