ANDROID: fscrypt: handle direct I/O with IV_INO_LBLK_32
With the existing fscrypt IV generation methods, each file's data blocks
have contiguous DUNs. Therefore the direct I/O code "just worked"
because it only submits logically contiguous bios. But with
IV_INO_LBLK_32, the direct I/O code breaks because the DUN can wrap from
0xffffffff to 0. We can't submit bios across such boundaries.
This is especially difficult to handle when block_size != PAGE_SIZE,
since in that case the DUN can wrap in the middle of a page. Punt on
this case for now and just handle block_size == PAGE_SIZE.
Add and use a new function fscrypt_dio_supported() to check whether a
direct I/O request is unsupported due to encryption constraints.
Then, update fs/direct-io.c (used by f2fs, and by ext4 in kernel v5.4
and earlier) and fs/iomap/direct-io.c (used by ext4 in kernel v5.5 and
later) to avoid submitting I/O across a DUN discontinuity.
(This is needed in ACK now because ACK already supports direct I/O with
inline crypto. I'll be sending this upstream along with the encrypted
direct I/O support itself once its prerequisites are closer to landing.)
(cherry picked from android-mainline commit
8d6c90c9d68b985fa809626d12f8c9aff3c9dcb1)
Conflicts:
fs/ext4/file.c
fs/iomap/direct-io.c
(Dropped the iomap changes because in kernel v5.4 and earlier,
ext4 doesn't use iomap for direct I/O)
Test: For now, just manually tested direct I/O on ext4 and f2fs in the
DUN discontinuity case.
Bug: 144046242
Change-Id: I0c0b0b20a73ade35c3660cc6f9c09d49d3853ba5
Signed-off-by: Eric Biggers <ebiggers@google.com>
Git-commit: cfc3461a04
Git-repo: https://android.googlesource.com/kernel/common/+/refs/heads/android-4.19
[neersoni@codeaurora.org: Back port the change]
Signed-off-by: Neeraj Soni <neersoni@codeaurora.org>
This commit is contained in:
parent
9e73b22a93
commit
1faab289c3
6 changed files with 123 additions and 13 deletions
|
@ -67,6 +67,14 @@ void fscrypt_free_bounce_page(struct page *bounce_page)
|
|||
}
|
||||
EXPORT_SYMBOL(fscrypt_free_bounce_page);
|
||||
|
||||
/*
|
||||
* Generate the IV for the given logical block number within the given file.
|
||||
* For filenames encryption, lblk_num == 0.
|
||||
*
|
||||
* Keep this in sync with fscrypt_limit_dio_pages(). fscrypt_limit_dio_pages()
|
||||
* needs to know about any IV generation methods where the low bits of IV don't
|
||||
* simply contain the lblk_num (e.g., IV_INO_LBLK_32).
|
||||
*/
|
||||
void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
|
||||
const struct fscrypt_info *ci)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/blkdev.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
|
@ -418,3 +419,84 @@ bool fscrypt_mergeable_bio_bh(struct bio *bio,
|
|||
return fscrypt_mergeable_bio(bio, inode, next_lblk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh);
|
||||
|
||||
/**
|
||||
* fscrypt_dio_supported() - check whether a direct I/O request is unsupported
|
||||
* due to encryption constraints
|
||||
* @iocb: the file and position the I/O is targeting
|
||||
* @iter: the I/O data segment(s)
|
||||
*
|
||||
* Return: true if direct I/O is supported
|
||||
*/
|
||||
bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter)
|
||||
{
|
||||
const struct inode *inode = file_inode(iocb->ki_filp);
|
||||
const struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
const unsigned int blocksize = i_blocksize(inode);
|
||||
|
||||
/* If the file is unencrypted, no veto from us. */
|
||||
if (!fscrypt_needs_contents_encryption(inode))
|
||||
return true;
|
||||
|
||||
/* We only support direct I/O with inline crypto, not fs-layer crypto */
|
||||
if (!fscrypt_inode_uses_inline_crypto(inode))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Since the granularity of encryption is filesystem blocks, the I/O
|
||||
* must be block aligned -- not just disk sector aligned.
|
||||
*/
|
||||
if (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter), blocksize))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* With IV_INO_LBLK_32 and sub-page blocks, the DUN can wrap around in
|
||||
* the middle of a page. This isn't handled by the direct I/O code yet.
|
||||
*/
|
||||
if (blocksize != PAGE_SIZE &&
|
||||
(fscrypt_policy_flags(&ci->ci_policy) &
|
||||
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
|
||||
|
||||
/**
|
||||
* fscrypt_limit_dio_pages() - limit I/O pages to avoid discontiguous DUNs
|
||||
* @inode: the file on which I/O is being done
|
||||
* @pos: the file position (in bytes) at which the I/O is being done
|
||||
* @nr_pages: the number of pages we want to submit starting at @pos
|
||||
*
|
||||
* For direct I/O: limit the number of pages that will be submitted in the bio
|
||||
* targeting @pos, in order to avoid crossing a data unit number (DUN)
|
||||
* discontinuity. This is only needed for certain IV generation methods.
|
||||
*
|
||||
* This assumes block_size == PAGE_SIZE; see fscrypt_dio_supported().
|
||||
*
|
||||
* Return: the actual number of pages that can be submitted
|
||||
*/
|
||||
int fscrypt_limit_dio_pages(const struct inode *inode, loff_t pos, int nr_pages)
|
||||
{
|
||||
const struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
u32 dun;
|
||||
|
||||
if (!fscrypt_inode_uses_inline_crypto(inode))
|
||||
return nr_pages;
|
||||
|
||||
if (nr_pages <= 1)
|
||||
return nr_pages;
|
||||
|
||||
if (!(fscrypt_policy_flags(&ci->ci_policy) &
|
||||
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
|
||||
return nr_pages;
|
||||
|
||||
if (WARN_ON_ONCE(i_blocksize(inode) != PAGE_SIZE))
|
||||
return 1;
|
||||
|
||||
/* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */
|
||||
|
||||
dun = ci->ci_hashed_ino + (pos >> inode->i_blkbits);
|
||||
|
||||
return min_t(u64, nr_pages, (u64)U32_MAX + 1 - dun);
|
||||
}
|
||||
|
|
|
@ -814,9 +814,17 @@ static inline int dio_send_cur_page(struct dio *dio, struct dio_submit *sdio,
|
|||
* current logical offset in the file does not equal what would
|
||||
* be the next logical offset in the bio, submit the bio we
|
||||
* have.
|
||||
*
|
||||
* When fscrypt inline encryption is used, data unit number
|
||||
* (DUN) contiguity is also required. Normally that's implied
|
||||
* by logical contiguity. However, certain IV generation
|
||||
* methods (e.g. IV_INO_LBLK_32) don't guarantee it. So, we
|
||||
* must explicitly check fscrypt_mergeable_bio() too.
|
||||
*/
|
||||
if (sdio->final_block_in_bio != sdio->cur_page_block ||
|
||||
cur_offset != bio_next_offset)
|
||||
cur_offset != bio_next_offset ||
|
||||
!fscrypt_mergeable_bio(sdio->bio, dio->inode,
|
||||
cur_offset >> dio->inode->i_blkbits))
|
||||
dio_bio_submit(dio, sdio);
|
||||
}
|
||||
|
||||
|
|
|
@ -3919,12 +3919,9 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
|||
ssize_t ret;
|
||||
int rw = iov_iter_rw(iter);
|
||||
|
||||
if (IS_ENABLED(CONFIG_FS_ENCRYPTION) && IS_ENCRYPTED(inode)) {
|
||||
if (!fscrypt_inode_uses_inline_crypto(inode) ||
|
||||
!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter),
|
||||
i_blocksize(inode)))
|
||||
return 0;
|
||||
}
|
||||
if (!fscrypt_dio_supported(iocb, iter))
|
||||
return 0;
|
||||
|
||||
if (fsverity_active(inode))
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -3971,12 +3971,8 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
|
|||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int rw = iov_iter_rw(iter);
|
||||
|
||||
if (IS_ENABLED(CONFIG_FS_ENCRYPTION) && f2fs_encrypted_file(inode)) {
|
||||
if (!fscrypt_inode_uses_inline_crypto(inode) ||
|
||||
!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter),
|
||||
F2FS_BLKSIZE))
|
||||
return true;
|
||||
}
|
||||
if (!fscrypt_dio_supported(iocb, iter))
|
||||
return true;
|
||||
if (fsverity_active(inode))
|
||||
return true;
|
||||
if (f2fs_is_multi_device(sbi))
|
||||
|
|
|
@ -531,6 +531,11 @@ extern bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
|
|||
extern bool fscrypt_mergeable_bio_bh(struct bio *bio,
|
||||
const struct buffer_head *next_bh);
|
||||
|
||||
bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter);
|
||||
|
||||
int fscrypt_limit_dio_pages(const struct inode *inode, loff_t pos,
|
||||
int nr_pages);
|
||||
|
||||
#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
|
||||
static inline bool fscrypt_inode_uses_inline_crypto(const struct inode *inode)
|
||||
{
|
||||
|
@ -563,6 +568,20 @@ static inline bool fscrypt_mergeable_bio_bh(struct bio *bio,
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_dio_supported(struct kiocb *iocb,
|
||||
struct iov_iter *iter)
|
||||
{
|
||||
const struct inode *inode = file_inode(iocb->ki_filp);
|
||||
|
||||
return !fscrypt_needs_contents_encryption(inode);
|
||||
}
|
||||
|
||||
static inline int fscrypt_limit_dio_pages(const struct inode *inode, loff_t pos,
|
||||
int nr_pages)
|
||||
{
|
||||
return nr_pages;
|
||||
}
|
||||
#endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_DM_DEFAULT_KEY)
|
||||
|
|
Loading…
Add table
Reference in a new issue