diff mbox series

[v14,078/138] mm/filemap: Add folio_mkwrite_check_truncate()

Message ID 20210715033704.692967-79-willy@infradead.org (mailing list archive)
State New
Headers show
Series Memory folios | expand

Commit Message

Matthew Wilcox July 15, 2021, 3:36 a.m. UTC
This is the folio equivalent of page_mkwrite_check_truncate().

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 include/linux/pagemap.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

Comments

David Howells Aug. 10, 2021, 9:30 p.m. UTC | #1
Matthew Wilcox (Oracle) <willy@infradead.org> wrote:

> This is the folio equivalent of page_mkwrite_check_truncate().
> 
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>

Reviewed-by: David Howells <dhowells@redhat.com>
Vlastimil Babka Aug. 12, 2021, 5:08 p.m. UTC | #2
On 7/15/21 5:36 AM, Matthew Wilcox (Oracle) wrote:
> This is the folio equivalent of page_mkwrite_check_truncate().
> 
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
>  include/linux/pagemap.h | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
> 
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index 412db88b8d0c..18c06c3e42c3 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -1121,6 +1121,34 @@ static inline unsigned long dir_pages(struct inode *inode)
>  			       PAGE_SHIFT;
>  }
>  
> +/**
> + * folio_mkwrite_check_truncate - check if folio was truncated
> + * @folio: the folio to check
> + * @inode: the inode to check the folio against
> + *
> + * Return: the number of bytes in the folio up to EOF,
> + * or -EFAULT if the folio was truncated.
> + */
> +static inline ssize_t folio_mkwrite_check_truncate(struct folio *folio,
> +					      struct inode *inode)
> +{
> +	loff_t size = i_size_read(inode);
> +	pgoff_t index = size >> PAGE_SHIFT;
> +	size_t offset = offset_in_folio(folio, size);
> +
> +	if (!folio->mapping)

The check in the page_ version is
if (page->mapping != inode->i_mapping)

Why is the one above sufficient?

> +		return -EFAULT;
> +
> +	/* folio is wholly inside EOF */
> +	if (folio_next_index(folio) - 1 < index)
> +		return folio_size(folio);
> +	/* folio is wholly past EOF */
> +	if (folio->index > index || !offset)
> +		return -EFAULT;
> +	/* folio is partially inside EOF */
> +	return offset;
> +}
> +
>  /**
>   * page_mkwrite_check_truncate - check if page was truncated
>   * @page: the page to check
>
Matthew Wilcox Aug. 15, 2021, 8:23 p.m. UTC | #3
On Thu, Aug 12, 2021 at 07:08:33PM +0200, Vlastimil Babka wrote:
> > +/**
> > + * folio_mkwrite_check_truncate - check if folio was truncated
> > + * @folio: the folio to check
> > + * @inode: the inode to check the folio against
> > + *
> > + * Return: the number of bytes in the folio up to EOF,
> > + * or -EFAULT if the folio was truncated.
> > + */
> > +static inline ssize_t folio_mkwrite_check_truncate(struct folio *folio,
> > +					      struct inode *inode)
> > +{
> > +	loff_t size = i_size_read(inode);
> > +	pgoff_t index = size >> PAGE_SHIFT;
> > +	size_t offset = offset_in_folio(folio, size);
> > +
> > +	if (!folio->mapping)
> 
> The check in the page_ version is
> if (page->mapping != inode->i_mapping)
> 
> Why is the one above sufficient?

Oh, good question!

We know that at some point this page belonged to this file.  The caller
has a reference on it (and at the time they acquired a refcount on the
page, the page was part of the file).  The caller also has the page
locked, but has not checked that the page is still part of the file.
That's where we come in.

The truncate path looks up the page, locks it, removes it from i_pages,
unmaps it, sets the page->mapping to NULL, unlocks it and puts the page.

Because the folio_mkwrite_check_truncate() caller holds a reference on
the page, the truncate path will not free the page.  So there are only
two possibilities for the value of page->mapping; either it's the same
as inode->i_mapping, or it's NULL.

Now, maybe this is a bit subtle.  For robustness, perhaps we should
check that it's definitely still part of this file instead of checking
whether it is currently part of no file.  Perhaps at some point in the
future, we might get the reference to the page without checking that
it's still part of this file.  Opinions?
diff mbox series

Patch

diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 412db88b8d0c..18c06c3e42c3 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -1121,6 +1121,34 @@  static inline unsigned long dir_pages(struct inode *inode)
 			       PAGE_SHIFT;
 }
 
+/**
+ * folio_mkwrite_check_truncate - check if folio was truncated
+ * @folio: the folio to check
+ * @inode: the inode to check the folio against
+ *
+ * Return: the number of bytes in the folio up to EOF,
+ * or -EFAULT if the folio was truncated.
+ */
+static inline ssize_t folio_mkwrite_check_truncate(struct folio *folio,
+					      struct inode *inode)
+{
+	loff_t size = i_size_read(inode);
+	pgoff_t index = size >> PAGE_SHIFT;
+	size_t offset = offset_in_folio(folio, size);
+
+	if (!folio->mapping)
+		return -EFAULT;
+
+	/* folio is wholly inside EOF */
+	if (folio_next_index(folio) - 1 < index)
+		return folio_size(folio);
+	/* folio is wholly past EOF */
+	if (folio->index > index || !offset)
+		return -EFAULT;
+	/* folio is partially inside EOF */
+	return offset;
+}
+
 /**
  * page_mkwrite_check_truncate - check if page was truncated
  * @page: the page to check