diff mbox series

fscrypt: Change fscrypt_encrypt_pagecache_blocks() to take a folio

Message ID 20250221051004.2951759-1-willy@infradead.org (mailing list archive)
State New
Headers show
Series fscrypt: Change fscrypt_encrypt_pagecache_blocks() to take a folio | expand

Commit Message

Matthew Wilcox Feb. 21, 2025, 5:10 a.m. UTC
ext4 and ceph already have a folio to pass; f2fs needs to be properly
converted but this will do for now.  This removes a reference
to page->index and page->mapping as well as removing a call to
compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 fs/ceph/addr.c          |  4 ++--
 fs/crypto/crypto.c      | 21 +++++++++------------
 fs/ext4/page-io.c       |  2 +-
 fs/f2fs/data.c          |  2 +-
 include/linux/fscrypt.h | 12 ++++--------
 5 files changed, 17 insertions(+), 24 deletions(-)

Comments

Eric Biggers Feb. 21, 2025, 5:16 a.m. UTC | #1
On Fri, Feb 21, 2025 at 05:10:01AM +0000, Matthew Wilcox (Oracle) wrote:
> ext4 and ceph already have a folio to pass; f2fs needs to be properly
> converted but this will do for now.  This removes a reference
> to page->index and page->mapping as well as removing a call to
> compound_head().
> 
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>

It's still assumed to be a small folio though, right?  It still just allocates a
"bounce page", not a "bounce folio".

- Eric
Matthew Wilcox Feb. 21, 2025, 5:35 a.m. UTC | #2
On Thu, Feb 20, 2025 at 09:16:07PM -0800, Eric Biggers wrote:
> On Fri, Feb 21, 2025 at 05:10:01AM +0000, Matthew Wilcox (Oracle) wrote:
> > ext4 and ceph already have a folio to pass; f2fs needs to be properly
> > converted but this will do for now.  This removes a reference
> > to page->index and page->mapping as well as removing a call to
> > compound_head().
> > 
> > Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> 
> It's still assumed to be a small folio though, right?  It still just allocates a
> "bounce page", not a "bounce folio".

Yup, I haven't figured out how to do large folio support, so any
filesystem using fscrypt can't support large folios for now.  I'm
working on "separate folio and page" at the moment rather than "enable
large folios everywhere".  Maybe someone else will figure out how to
support large folios in fscrypt and I won't have to ;-)
Eric Biggers Feb. 21, 2025, 9:09 p.m. UTC | #3
On Fri, Feb 21, 2025 at 05:35:09AM +0000, Matthew Wilcox wrote:
> On Thu, Feb 20, 2025 at 09:16:07PM -0800, Eric Biggers wrote:
> > On Fri, Feb 21, 2025 at 05:10:01AM +0000, Matthew Wilcox (Oracle) wrote:
> > > ext4 and ceph already have a folio to pass; f2fs needs to be properly
> > > converted but this will do for now.  This removes a reference
> > > to page->index and page->mapping as well as removing a call to
> > > compound_head().
> > > 
> > > Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> > 
> > It's still assumed to be a small folio though, right?  It still just allocates a
> > "bounce page", not a "bounce folio".
> 
> Yup, I haven't figured out how to do large folio support, so any
> filesystem using fscrypt can't support large folios for now.  I'm
> working on "separate folio and page" at the moment rather than "enable
> large folios everywhere".  

It might be a good idea to make the limitation to small folios clear in the
function's kerneldoc and/or in a WARN_ON_ONCE().

> Maybe someone else will figure out how to
> support large folios in fscrypt and I won't have to ;-)

Decryption is easy and already done, but encryption is harder.  We have to
encrypt into bounce pages, since we can't overwrite the plaintext in the page
cache.  And when encrypting a large folio, I don't think we can rely on being
able to allocate a large bounce folio of the same size; it may just not be
available at the time.  So we'd still need bounce pages, and the filesystem
would have to keep track of potentially multiple bounce pages per folio.

However, this is all specific to the original fs-layer file contents encryption
path.  The newer inline crypto one should just work, even without hardware
support since block/blk-crypto-fallback.c would be used.  (blk-crypto-fallback
operates at the bio level, handles en/decryption, and manages bounce pages
itself.)  It actually might be time to remove the fs-layer file contents
encryption path and just support the inline crypto one.

- Eric
Matthew Wilcox Feb. 21, 2025, 10:22 p.m. UTC | #4
On Fri, Feb 21, 2025 at 09:09:38PM +0000, Eric Biggers wrote:
> > Yup, I haven't figured out how to do large folio support, so any
> > filesystem using fscrypt can't support large folios for now.  I'm
> > working on "separate folio and page" at the moment rather than "enable
> > large folios everywhere".  
> 
> It might be a good idea to make the limitation to small folios clear in the
> function's kerneldoc and/or in a WARN_ON_ONCE().

I can add a VM_BUG_ON like the other
this-is-not-yet-ready-for-large-folios tests.

> > Maybe someone else will figure out how to
> > support large folios in fscrypt and I won't have to ;-)
> 
> Decryption is easy and already done, but encryption is harder.  We have to
> encrypt into bounce pages, since we can't overwrite the plaintext in the page
> cache.  And when encrypting a large folio, I don't think we can rely on being
> able to allocate a large bounce folio of the same size; it may just not be
> available at the time.  So we'd still need bounce pages, and the filesystem
> would have to keep track of potentially multiple bounce pages per folio.
> 
> However, this is all specific to the original fs-layer file contents encryption
> path.  The newer inline crypto one should just work, even without hardware
> support since block/blk-crypto-fallback.c would be used.  (blk-crypto-fallback
> operates at the bio level, handles en/decryption, and manages bounce pages
> itself.)  It actually might be time to remove the fs-layer file contents
> encryption path and just support the inline crypto one.

That should be fine for f2fs and ext4, but ceph might be unhappy since
it doesn't use bios.  Would we need an analagous function for network
filesystems?
Eric Biggers Feb. 21, 2025, 10:41 p.m. UTC | #5
On Fri, Feb 21, 2025 at 10:22:54PM +0000, Matthew Wilcox wrote:
> On Fri, Feb 21, 2025 at 09:09:38PM +0000, Eric Biggers wrote:
> > > Yup, I haven't figured out how to do large folio support, so any
> > > filesystem using fscrypt can't support large folios for now.  I'm
> > > working on "separate folio and page" at the moment rather than "enable
> > > large folios everywhere".  
> > 
> > It might be a good idea to make the limitation to small folios clear in the
> > function's kerneldoc and/or in a WARN_ON_ONCE().
> 
> I can add a VM_BUG_ON like the other
> this-is-not-yet-ready-for-large-folios tests.
> 
> > > Maybe someone else will figure out how to
> > > support large folios in fscrypt and I won't have to ;-)
> > 
> > Decryption is easy and already done, but encryption is harder.  We have to
> > encrypt into bounce pages, since we can't overwrite the plaintext in the page
> > cache.  And when encrypting a large folio, I don't think we can rely on being
> > able to allocate a large bounce folio of the same size; it may just not be
> > available at the time.  So we'd still need bounce pages, and the filesystem
> > would have to keep track of potentially multiple bounce pages per folio.
> > 
> > However, this is all specific to the original fs-layer file contents encryption
> > path.  The newer inline crypto one should just work, even without hardware
> > support since block/blk-crypto-fallback.c would be used.  (blk-crypto-fallback
> > operates at the bio level, handles en/decryption, and manages bounce pages
> > itself.)  It actually might be time to remove the fs-layer file contents
> > encryption path and just support the inline crypto one.
> 
> That should be fine for f2fs and ext4, but ceph might be unhappy since
> it doesn't use bios.  Would we need an analagous function for network
> filesystems?

Oof, I forgot about ceph again.  Something similar applies to ubifs (it's not
block based), but ubifs uses its own bounce buffers, so it never calls
fscrypt_encrypt_pagecache_blocks().  ceph does call it, though.  So yes, while
moving to "block based filesystems will always use inline crypto" would simplify
some of fs/crypto/ and allow block-based filesystems to use large folios on
encrypted files, ceph would need a different solution.  I think it still has to
involve encrypting the folio's data into a list of bounce pages, keeping track
of them during the write, and freeing them at the end of the write.  So we'd
have to look at what is the easiest way to do that in the ceph case.

- Eric
diff mbox series

Patch

diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index f5224a566b69..9261cd690181 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -753,7 +753,7 @@  static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
 	ceph_fscache_write_to_cache(inode, page_off, len, caching);
 
 	if (IS_ENCRYPTED(inode)) {
-		bounce_page = fscrypt_encrypt_pagecache_blocks(page,
+		bounce_page = fscrypt_encrypt_pagecache_blocks(folio,
 						    CEPH_FSCRYPT_BLOCK_SIZE, 0,
 						    GFP_NOFS);
 		if (IS_ERR(bounce_page)) {
@@ -1186,7 +1186,7 @@  static int ceph_writepages_start(struct address_space *mapping,
 
 			if (IS_ENCRYPTED(inode)) {
 				pages[locked_pages] =
-					fscrypt_encrypt_pagecache_blocks(page,
+					fscrypt_encrypt_pagecache_blocks(folio,
 						PAGE_SIZE, 0,
 						locked_pages ? GFP_NOWAIT : GFP_NOFS);
 				if (IS_ERR(pages[locked_pages])) {
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 328470d40dec..1fbf523632fb 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -153,8 +153,8 @@  int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci,
 }
 
 /**
- * fscrypt_encrypt_pagecache_blocks() - Encrypt data from a pagecache page
- * @page: the locked pagecache page containing the data to encrypt
+ * fscrypt_encrypt_pagecache_blocks() - Encrypt data from a pagecache folio
+ * @folio: the locked pagecache folio containing the data to encrypt
  * @len: size of the data to encrypt, in bytes
  * @offs: offset within @page of the data to encrypt, in bytes
  * @gfp_flags: memory allocation flags; see details below
@@ -177,23 +177,20 @@  int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci,
  *
  * Return: the new encrypted bounce page on success; an ERR_PTR() on failure
  */
-struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
-					      unsigned int len,
-					      unsigned int offs,
-					      gfp_t gfp_flags)
-
+struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,
+		size_t len, size_t offs, gfp_t gfp_flags)
 {
-	const struct inode *inode = page->mapping->host;
+	const struct inode *inode = folio->mapping->host;
 	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
 	const unsigned int du_bits = ci->ci_data_unit_bits;
 	const unsigned int du_size = 1U << du_bits;
 	struct page *ciphertext_page;
-	u64 index = ((u64)page->index << (PAGE_SHIFT - du_bits)) +
+	u64 index = ((u64)folio->index << (PAGE_SHIFT - du_bits)) +
 		    (offs >> du_bits);
 	unsigned int i;
 	int err;
 
-	if (WARN_ON_ONCE(!PageLocked(page)))
+	if (WARN_ON_ONCE(!folio_test_locked(folio)))
 		return ERR_PTR(-EINVAL);
 
 	if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, du_size)))
@@ -205,7 +202,7 @@  struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
 
 	for (i = offs; i < offs + len; i += du_size, index++) {
 		err = fscrypt_crypt_data_unit(ci, FS_ENCRYPT, index,
-					      page, ciphertext_page,
+					      &folio->page, ciphertext_page,
 					      du_size, i, gfp_flags);
 		if (err) {
 			fscrypt_free_bounce_page(ciphertext_page);
@@ -213,7 +210,7 @@  struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
 		}
 	}
 	SetPagePrivate(ciphertext_page);
-	set_page_private(ciphertext_page, (unsigned long)page);
+	set_page_private(ciphertext_page, (unsigned long)folio);
 	return ciphertext_page;
 }
 EXPORT_SYMBOL(fscrypt_encrypt_pagecache_blocks);
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 69b8a7221a2b..37abee5016c3 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -522,7 +522,7 @@  int ext4_bio_write_folio(struct ext4_io_submit *io, struct folio *folio,
 		if (io->io_bio)
 			gfp_flags = GFP_NOWAIT | __GFP_NOWARN;
 	retry_encrypt:
-		bounce_page = fscrypt_encrypt_pagecache_blocks(&folio->page,
+		bounce_page = fscrypt_encrypt_pagecache_blocks(folio,
 					enc_bytes, 0, gfp_flags);
 		if (IS_ERR(bounce_page)) {
 			ret = PTR_ERR(bounce_page);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 24c5cb1f5ada..b6857b4a9787 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2504,7 +2504,7 @@  int f2fs_encrypt_one_page(struct f2fs_io_info *fio)
 		return 0;
 
 retry_encrypt:
-	fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(page,
+	fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(page_folio(page),
 					PAGE_SIZE, 0, gfp_flags);
 	if (IS_ERR(fio->encrypted_page)) {
 		/* flush pending IOs and wait for a while in the ENOMEM case */
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 18855cb44b1c..56fad33043d5 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -310,10 +310,8 @@  static inline void fscrypt_prepare_dentry(struct dentry *dentry,
 /* crypto.c */
 void fscrypt_enqueue_decrypt_work(struct work_struct *);
 
-struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
-					      unsigned int len,
-					      unsigned int offs,
-					      gfp_t gfp_flags);
+struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,
+		size_t len, size_t offs, gfp_t gfp_flags);
 int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
 				  unsigned int len, unsigned int offs,
 				  u64 lblk_num, gfp_t gfp_flags);
@@ -480,10 +478,8 @@  static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work)
 {
 }
 
-static inline struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
-							    unsigned int len,
-							    unsigned int offs,
-							    gfp_t gfp_flags)
+static inline struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,
+		size_t len, size_t offs, gfp_t gfp_flags)
 {
 	return ERR_PTR(-EOPNOTSUPP);
 }