diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 776ddc655f79..927d3c88816b 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -184,6 +184,9 @@ replaced with HKDF or another more standard KDF in the future.
 Encryption modes and usage
 ==========================
 
+Available modes
+---------------
+
 fscrypt allows one encryption mode to be specified for file contents
 and one encryption mode to be specified for filenames.  Different
 directory trees are permitted to use different encryption modes.
@@ -191,24 +194,52 @@ Currently, the following pairs of encryption modes are supported:
 
 - AES-256-XTS for contents and AES-256-CTS-CBC for filenames
 - AES-128-CBC for contents and AES-128-CTS-CBC for filenames
+- ChaCha20 for contents and AES-256-CTS-CBC for filenames
 
 It is strongly recommended to use AES-256-XTS for contents encryption.
 AES-128-CBC was added only for low-powered embedded devices with
 crypto accelerators such as CAAM or CESA that do not support XTS.
 
+Similarly, ChaCha20 was only added for low-end devices that have
+neither a CPU with AES instructions, nor a hardware crypto
+accelerator.  Note that since ChaCha20 is a stream cipher, it is
+easily broken if an attacker can view encrypted data both before and
+after it is overwritten.  Thus, even moreso than the other modes,
+ChaCha20 can protect data confidentiality *only* in the event of a
+single point-in-time, permanent offline compromise of the storage.
+Also, ChaCha20 is supported only for contents encryption, not
+filenames encryption, because all filenames in a directory have to be
+encrypted with the same IV, which would be especially inappropriate
+for a stream cipher.
+
 New encryption modes can be added relatively easily, without changes
 to individual filesystems.  However, authenticated encryption (AE)
 modes are not currently supported because of the difficulty of dealing
 with ciphertext expansion.
 
+Contents encryption
+-------------------
+
 For file contents, each filesystem block is encrypted independently.
 Currently, only the case where the filesystem block size is equal to
-the system's page size (usually 4096 bytes) is supported.  With the
-XTS mode of operation (recommended), the logical block number within
-the file is used as the IV.  With the CBC mode of operation (not
-recommended), ESSIV is used; specifically, the IV for CBC is the
-logical block number encrypted with AES-256, where the AES-256 key is
-the SHA-256 hash of the inode's data encryption key.
+the system's page size (usually 4096 bytes) is supported.
+
+With the XTS mode of operation, the logical block number within the
+file is used as the IV.
+
+With the CBC mode of operation, ESSIV is used.  Specifically, the IV
+is the file logical block number encrypted with AES-256, where the
+AES-256 key is the SHA-256 hash of the inode's data encryption key.
+
+With ChaCha20, the file logical block number is also used as the IV,
+but it is formatted differently to ensure that it is copied into the
+"nonce" portion of the ChaCha20 initial state (words 14-15) rather
+than the "block counter" portion (words 12-13).  This detail is
+critical, since otherwise different portions of the file would be
+encrypted with the same keystream.
+
+Filenames encryption
+--------------------
 
 For filenames, the full filename is encrypted at once.  Because of the
 requirements to retain support for efficient directory lookups and
diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig
index 02b7d91c9231..44f052e9d842 100644
--- a/fs/crypto/Kconfig
+++ b/fs/crypto/Kconfig
@@ -3,6 +3,7 @@ config FS_ENCRYPTION
 	select CRYPTO
 	select CRYPTO_AES
 	select CRYPTO_CBC
+	select CRYPTO_CHACHA20
 	select CRYPTO_ECB
 	select CRYPTO_XTS
 	select CRYPTO_CTS
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 732a786cce9d..d5c95a18db59 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -126,15 +126,66 @@ struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags)
 }
 EXPORT_SYMBOL(fscrypt_get_ctx);
 
+struct fscrypt_iv {
+	__le64 first_half;
+	__le64 second_half;
+};
+
+/*
+ * Generate the IV for encrypting/decrypting the block at the given logical
+ * block number within a file.
+ */
+static void fscrypt_generate_iv(struct fscrypt_iv *iv, u64 lblk_num,
+				const struct fscrypt_info *ci)
+{
+	BUILD_BUG_ON(sizeof(*iv) != FS_IV_SIZE);
+
+	if (ci->ci_data_mode == FS_ENCRYPTION_MODE_CHACHA20) {
+		/*
+		 * ChaCha20 interprets its IV as a block counter followed by a
+		 * nonce.  We *MUST NOT* use the file logical block number as
+		 * the Chacha block counter because the ChaCha block counter
+		 * counts 64-byte ChaCha blocks, which are much smaller than
+		 * file blocks.  If we did, then portions of the keystream would
+		 * be repeated, which would be catastrophic.
+		 *
+		 * We could initialize the block counter with the offset in the
+		 * file in ChaCha blocks.  But RFC7539 defines the ChaCha20
+		 * block counter to be 32-bit, which is only enough for a 256GiB
+		 * keystream.  Confusingly, this differs from the original
+		 * ChaCha paper which defines the block counter to be 64-bit.
+		 *
+		 * To be compatible with either convention, just put the file
+		 * logical block number in the second half of the IV, so that it
+		 * goes into the "nonce" portion of the ChaCha initial state
+		 * (words 14-15).  The ChaCha block counter then starts at 0 for
+		 * each file block.  In other words, we use one keystream per
+		 * file block instead of one keystream per file.
+		 */
+		iv->first_half = 0;
+		iv->second_half = cpu_to_le64(lblk_num);
+
+		BUG_ON(ci->ci_essiv_tfm != NULL);
+	} else {
+		/* XTS or CBC */
+		iv->first_half = cpu_to_le64(lblk_num);
+		iv->second_half = 0;
+
+		if (ci->ci_essiv_tfm != NULL) {
+			/* CBC */
+			BUILD_BUG_ON(AES_BLOCK_SIZE != FS_IV_SIZE);
+			crypto_cipher_encrypt_one(ci->ci_essiv_tfm,
+						  (u8 *)iv, (u8 *)iv);
+		}
+	}
+}
+
 int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw,
 			   u64 lblk_num, struct page *src_page,
 			   struct page *dest_page, unsigned int len,
 			   unsigned int offs, gfp_t gfp_flags)
 {
-	struct {
-		__le64 index;
-		u8 padding[FS_IV_SIZE - sizeof(__le64)];
-	} iv;
+	struct fscrypt_iv iv;
 	struct skcipher_request *req = NULL;
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist dst, src;
@@ -144,15 +195,7 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw,
 
 	BUG_ON(len == 0);
 
-	BUILD_BUG_ON(sizeof(iv) != FS_IV_SIZE);
-	BUILD_BUG_ON(AES_BLOCK_SIZE != FS_IV_SIZE);
-	iv.index = cpu_to_le64(lblk_num);
-	memset(iv.padding, 0, sizeof(iv.padding));
-
-	if (ci->ci_essiv_tfm != NULL) {
-		crypto_cipher_encrypt_one(ci->ci_essiv_tfm, (u8 *)&iv,
-					  (u8 *)&iv);
-	}
+	fscrypt_generate_iv(&iv, lblk_num, ci);
 
 	req = skcipher_request_alloc(tfm, gfp_flags);
 	if (!req) {
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index 5e6e846f5a24..bae4cce2389a 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -13,6 +13,7 @@
 #include <linux/scatterlist.h>
 #include <linux/ratelimit.h>
 #include <crypto/aes.h>
+#include <crypto/chacha20.h>
 #include <crypto/sha.h>
 #include "fscrypt_private.h"
 
@@ -134,6 +135,7 @@ static const struct {
 					     FS_AES_128_CBC_KEY_SIZE },
 	[FS_ENCRYPTION_MODE_AES_128_CTS] = { "cts(cbc(aes))",
 					     FS_AES_128_CTS_KEY_SIZE },
+	[FS_ENCRYPTION_MODE_CHACHA20]	 = { "chacha20", CHACHA20_KEY_SIZE },
 };
 
 static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode,
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 08b4b40c5aa8..1cfb7950f915 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -100,11 +100,15 @@ static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
 static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
 					u32 filenames_mode)
 {
+	if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
+	    filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
+		return true;
+
 	if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC &&
 	    filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS)
 		return true;
 
-	if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
+	if (contents_mode == FS_ENCRYPTION_MODE_CHACHA20 &&
 	    filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
 		return true;
 
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 4199f8acbce5..5a25eb2994b1 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -275,6 +275,7 @@ struct fsxattr {
 #define FS_ENCRYPTION_MODE_AES_256_CTS		4
 #define FS_ENCRYPTION_MODE_AES_128_CBC		5
 #define FS_ENCRYPTION_MODE_AES_128_CTS		6
+#define FS_ENCRYPTION_MODE_CHACHA20		7
 
 struct fscrypt_policy {
 	__u8 version;
