Skip to content

Commit 1896ce8

Browse files
committed
Merge tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux
Pull interleaved SHA-256 hashing support from Eric Biggers: "Optimize fsverity with 2-way interleaved hashing Add support for 2-way interleaved SHA-256 hashing to lib/crypto/, and make fsverity use it for faster file data verification. This improves fsverity performance on many x86_64 and arm64 processors. Later, I plan to make dm-verity use this too" * tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux: fsverity: Use 2-way interleaved SHA-256 hashing when supported fsverity: Remove inode parameter from fsverity_hash_block() lib/crypto: tests: Add tests and benchmark for sha256_finup_2x() lib/crypto: x86/sha256: Add support for 2-way interleaved hashing lib/crypto: arm64/sha256: Add support for 2-way interleaved hashing lib/crypto: sha256: Add support for 2-way interleaved hashing
2 parents d8768fb + a1f692f commit 1896ce8

11 files changed

Lines changed: 1147 additions & 56 deletions

File tree

fs/verity/enable.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ struct block_buffer {
1919
};
2020

2121
/* Hash a block, writing the result to the next level's pending block buffer. */
22-
static int hash_one_block(struct inode *inode,
23-
const struct merkle_tree_params *params,
22+
static int hash_one_block(const struct merkle_tree_params *params,
2423
struct block_buffer *cur)
2524
{
2625
struct block_buffer *next = cur + 1;
@@ -36,8 +35,7 @@ static int hash_one_block(struct inode *inode,
3635
/* Zero-pad the block if it's shorter than the block size. */
3736
memset(&cur->data[cur->filled], 0, params->block_size - cur->filled);
3837

39-
fsverity_hash_block(params, inode, cur->data,
40-
&next->data[next->filled]);
38+
fsverity_hash_block(params, cur->data, &next->data[next->filled]);
4139
next->filled += params->digest_size;
4240
cur->filled = 0;
4341
return 0;
@@ -123,7 +121,7 @@ static int build_merkle_tree(struct file *filp,
123121
fsverity_err(inode, "Short read of file data");
124122
goto out;
125123
}
126-
err = hash_one_block(inode, params, &buffers[-1]);
124+
err = hash_one_block(params, &buffers[-1]);
127125
if (err)
128126
goto out;
129127
for (level = 0; level < num_levels; level++) {
@@ -134,7 +132,7 @@ static int build_merkle_tree(struct file *filp,
134132
}
135133
/* Next block at @level is full */
136134

137-
err = hash_one_block(inode, params, &buffers[level]);
135+
err = hash_one_block(params, &buffers[level]);
138136
if (err)
139137
goto out;
140138
err = write_merkle_tree_block(inode,
@@ -154,7 +152,7 @@ static int build_merkle_tree(struct file *filp,
154152
/* Finish all nonempty pending tree blocks. */
155153
for (level = 0; level < num_levels; level++) {
156154
if (buffers[level].filled != 0) {
157-
err = hash_one_block(inode, params, &buffers[level]);
155+
err = hash_one_block(params, &buffers[level]);
158156
if (err)
159157
goto out;
160158
err = write_merkle_tree_block(inode,

fs/verity/fsverity_private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ union fsverity_hash_ctx *
9090
fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
9191
const u8 *salt, size_t salt_size);
9292
void fsverity_hash_block(const struct merkle_tree_params *params,
93-
const struct inode *inode, const void *data, u8 *out);
93+
const void *data, u8 *out);
9494
void fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
9595
const void *data, size_t size, u8 *out);
9696
void __init fsverity_check_hash_algs(void);

fs/verity/hash_algs.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,14 @@ fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
9494
/**
9595
* fsverity_hash_block() - hash a single data or hash block
9696
* @params: the Merkle tree's parameters
97-
* @inode: inode for which the hashing is being done
9897
* @data: virtual address of a buffer containing the block to hash
9998
* @out: output digest, size 'params->digest_size' bytes
10099
*
101100
* Hash a single data or hash block. The hash is salted if a salt is specified
102101
* in the Merkle tree parameters.
103102
*/
104103
void fsverity_hash_block(const struct merkle_tree_params *params,
105-
const struct inode *inode, const void *data, u8 *out)
104+
const void *data, u8 *out)
106105
{
107106
union fsverity_hash_ctx ctx;
108107

fs/verity/verify.c

Lines changed: 140 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@
1010
#include <linux/bio.h>
1111
#include <linux/export.h>
1212

13+
#define FS_VERITY_MAX_PENDING_BLOCKS 2
14+
15+
struct fsverity_pending_block {
16+
const void *data;
17+
u64 pos;
18+
u8 real_hash[FS_VERITY_MAX_DIGEST_SIZE];
19+
};
20+
21+
struct fsverity_verification_context {
22+
struct inode *inode;
23+
struct fsverity_info *vi;
24+
unsigned long max_ra_pages;
25+
26+
/*
27+
* This is the queue of data blocks that are pending verification. When
28+
* the crypto layer supports interleaved hashing, we allow multiple
29+
* blocks to be queued up in order to utilize it. This can improve
30+
* performance significantly vs. sequential hashing of each block.
31+
*/
32+
int num_pending;
33+
int max_pending;
34+
struct fsverity_pending_block
35+
pending_blocks[FS_VERITY_MAX_PENDING_BLOCKS];
36+
};
37+
1338
static struct workqueue_struct *fsverity_read_workqueue;
1439

1540
/*
@@ -79,7 +104,7 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
79104
}
80105

81106
/*
82-
* Verify a single data block against the file's Merkle tree.
107+
* Verify the hash of a single data block against the file's Merkle tree.
83108
*
84109
* In principle, we need to verify the entire path to the root node. However,
85110
* for efficiency the filesystem may cache the hash blocks. Therefore we need
@@ -88,10 +113,11 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
88113
*
89114
* Return: %true if the data block is valid, else %false.
90115
*/
91-
static bool
92-
verify_data_block(struct inode *inode, struct fsverity_info *vi,
93-
const void *data, u64 data_pos, unsigned long max_ra_pages)
116+
static bool verify_data_block(struct inode *inode, struct fsverity_info *vi,
117+
const struct fsverity_pending_block *dblock,
118+
unsigned long max_ra_pages)
94119
{
120+
const u64 data_pos = dblock->pos;
95121
const struct merkle_tree_params *params = &vi->tree_params;
96122
const unsigned int hsize = params->digest_size;
97123
int level;
@@ -115,8 +141,12 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
115141
*/
116142
u64 hidx = data_pos >> params->log_blocksize;
117143

118-
/* Up to 1 + FS_VERITY_MAX_LEVELS pages may be mapped at once */
119-
BUILD_BUG_ON(1 + FS_VERITY_MAX_LEVELS > KM_MAX_IDX);
144+
/*
145+
* Up to FS_VERITY_MAX_PENDING_BLOCKS + FS_VERITY_MAX_LEVELS pages may
146+
* be mapped at once.
147+
*/
148+
static_assert(FS_VERITY_MAX_PENDING_BLOCKS + FS_VERITY_MAX_LEVELS <=
149+
KM_MAX_IDX);
120150

121151
if (unlikely(data_pos >= inode->i_size)) {
122152
/*
@@ -127,7 +157,7 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
127157
* any part past EOF should be all zeroes. Therefore, we need
128158
* to verify that any data blocks fully past EOF are all zeroes.
129159
*/
130-
if (memchr_inv(data, 0, params->block_size)) {
160+
if (memchr_inv(dblock->data, 0, params->block_size)) {
131161
fsverity_err(inode,
132162
"FILE CORRUPTED! Data past EOF is not zeroed");
133163
return false;
@@ -202,7 +232,7 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
202232
unsigned long hblock_idx = hblocks[level - 1].index;
203233
unsigned int hoffset = hblocks[level - 1].hoffset;
204234

205-
fsverity_hash_block(params, inode, haddr, real_hash);
235+
fsverity_hash_block(params, haddr, real_hash);
206236
if (memcmp(want_hash, real_hash, hsize) != 0)
207237
goto corrupted;
208238
/*
@@ -220,18 +250,18 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
220250
put_page(hpage);
221251
}
222252

223-
/* Finally, verify the data block. */
224-
fsverity_hash_block(params, inode, data, real_hash);
225-
if (memcmp(want_hash, real_hash, hsize) != 0)
253+
/* Finally, verify the hash of the data block. */
254+
if (memcmp(want_hash, dblock->real_hash, hsize) != 0)
226255
goto corrupted;
227256
return true;
228257

229258
corrupted:
230-
fsverity_err(inode,
231-
"FILE CORRUPTED! pos=%llu, level=%d, want_hash=%s:%*phN, real_hash=%s:%*phN",
232-
data_pos, level - 1,
233-
params->hash_alg->name, hsize, want_hash,
234-
params->hash_alg->name, hsize, real_hash);
259+
fsverity_err(
260+
inode,
261+
"FILE CORRUPTED! pos=%llu, level=%d, want_hash=%s:%*phN, real_hash=%s:%*phN",
262+
data_pos, level - 1, params->hash_alg->name, hsize, want_hash,
263+
params->hash_alg->name, hsize,
264+
level == 0 ? dblock->real_hash : real_hash);
235265
error:
236266
for (; level > 0; level--) {
237267
kunmap_local(hblocks[level - 1].addr);
@@ -240,13 +270,73 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi,
240270
return false;
241271
}
242272

243-
static bool
244-
verify_data_blocks(struct folio *data_folio, size_t len, size_t offset,
245-
unsigned long max_ra_pages)
273+
static void
274+
fsverity_init_verification_context(struct fsverity_verification_context *ctx,
275+
struct inode *inode,
276+
unsigned long max_ra_pages)
246277
{
247-
struct inode *inode = data_folio->mapping->host;
248278
struct fsverity_info *vi = *fsverity_info_addr(inode);
249-
const unsigned int block_size = vi->tree_params.block_size;
279+
280+
ctx->inode = inode;
281+
ctx->vi = vi;
282+
ctx->max_ra_pages = max_ra_pages;
283+
ctx->num_pending = 0;
284+
if (vi->tree_params.hash_alg->algo_id == HASH_ALGO_SHA256 &&
285+
sha256_finup_2x_is_optimized())
286+
ctx->max_pending = 2;
287+
else
288+
ctx->max_pending = 1;
289+
}
290+
291+
static void
292+
fsverity_clear_pending_blocks(struct fsverity_verification_context *ctx)
293+
{
294+
int i;
295+
296+
for (i = ctx->num_pending - 1; i >= 0; i--) {
297+
kunmap_local(ctx->pending_blocks[i].data);
298+
ctx->pending_blocks[i].data = NULL;
299+
}
300+
ctx->num_pending = 0;
301+
}
302+
303+
static bool
304+
fsverity_verify_pending_blocks(struct fsverity_verification_context *ctx)
305+
{
306+
struct fsverity_info *vi = ctx->vi;
307+
const struct merkle_tree_params *params = &vi->tree_params;
308+
int i;
309+
310+
if (ctx->num_pending == 2) {
311+
/* num_pending == 2 implies that the algorithm is SHA-256 */
312+
sha256_finup_2x(params->hashstate ? &params->hashstate->sha256 :
313+
NULL,
314+
ctx->pending_blocks[0].data,
315+
ctx->pending_blocks[1].data, params->block_size,
316+
ctx->pending_blocks[0].real_hash,
317+
ctx->pending_blocks[1].real_hash);
318+
} else {
319+
for (i = 0; i < ctx->num_pending; i++)
320+
fsverity_hash_block(params, ctx->pending_blocks[i].data,
321+
ctx->pending_blocks[i].real_hash);
322+
}
323+
324+
for (i = 0; i < ctx->num_pending; i++) {
325+
if (!verify_data_block(ctx->inode, vi, &ctx->pending_blocks[i],
326+
ctx->max_ra_pages))
327+
return false;
328+
}
329+
fsverity_clear_pending_blocks(ctx);
330+
return true;
331+
}
332+
333+
static bool fsverity_add_data_blocks(struct fsverity_verification_context *ctx,
334+
struct folio *data_folio, size_t len,
335+
size_t offset)
336+
{
337+
struct fsverity_info *vi = ctx->vi;
338+
const struct merkle_tree_params *params = &vi->tree_params;
339+
const unsigned int block_size = params->block_size;
250340
u64 pos = (u64)data_folio->index << PAGE_SHIFT;
251341

252342
if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offset, block_size)))
@@ -255,14 +345,11 @@ verify_data_blocks(struct folio *data_folio, size_t len, size_t offset,
255345
folio_test_uptodate(data_folio)))
256346
return false;
257347
do {
258-
void *data;
259-
bool valid;
260-
261-
data = kmap_local_folio(data_folio, offset);
262-
valid = verify_data_block(inode, vi, data, pos + offset,
263-
max_ra_pages);
264-
kunmap_local(data);
265-
if (!valid)
348+
ctx->pending_blocks[ctx->num_pending].data =
349+
kmap_local_folio(data_folio, offset);
350+
ctx->pending_blocks[ctx->num_pending].pos = pos + offset;
351+
if (++ctx->num_pending == ctx->max_pending &&
352+
!fsverity_verify_pending_blocks(ctx))
266353
return false;
267354
offset += block_size;
268355
len -= block_size;
@@ -284,7 +371,15 @@ verify_data_blocks(struct folio *data_folio, size_t len, size_t offset,
284371
*/
285372
bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset)
286373
{
287-
return verify_data_blocks(folio, len, offset, 0);
374+
struct fsverity_verification_context ctx;
375+
376+
fsverity_init_verification_context(&ctx, folio->mapping->host, 0);
377+
378+
if (fsverity_add_data_blocks(&ctx, folio, len, offset) &&
379+
fsverity_verify_pending_blocks(&ctx))
380+
return true;
381+
fsverity_clear_pending_blocks(&ctx);
382+
return false;
288383
}
289384
EXPORT_SYMBOL_GPL(fsverity_verify_blocks);
290385

@@ -305,6 +400,8 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks);
305400
*/
306401
void fsverity_verify_bio(struct bio *bio)
307402
{
403+
struct inode *inode = bio_first_folio_all(bio)->mapping->host;
404+
struct fsverity_verification_context ctx;
308405
struct folio_iter fi;
309406
unsigned long max_ra_pages = 0;
310407

@@ -321,13 +418,21 @@ void fsverity_verify_bio(struct bio *bio)
321418
max_ra_pages = bio->bi_iter.bi_size >> (PAGE_SHIFT + 2);
322419
}
323420

421+
fsverity_init_verification_context(&ctx, inode, max_ra_pages);
422+
324423
bio_for_each_folio_all(fi, bio) {
325-
if (!verify_data_blocks(fi.folio, fi.length, fi.offset,
326-
max_ra_pages)) {
327-
bio->bi_status = BLK_STS_IOERR;
328-
break;
329-
}
424+
if (!fsverity_add_data_blocks(&ctx, fi.folio, fi.length,
425+
fi.offset))
426+
goto ioerr;
330427
}
428+
429+
if (!fsverity_verify_pending_blocks(&ctx))
430+
goto ioerr;
431+
return;
432+
433+
ioerr:
434+
fsverity_clear_pending_blocks(&ctx);
435+
bio->bi_status = BLK_STS_IOERR;
331436
}
332437
EXPORT_SYMBOL_GPL(fsverity_verify_bio);
333438
#endif /* CONFIG_BLOCK */

include/crypto/sha2.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,34 @@ void sha256_final(struct sha256_ctx *ctx, u8 out[SHA256_DIGEST_SIZE]);
375375
*/
376376
void sha256(const u8 *data, size_t len, u8 out[SHA256_DIGEST_SIZE]);
377377

378+
/**
379+
* sha256_finup_2x() - Compute two SHA-256 digests from a common initial
380+
* context. On some CPUs, this is faster than sequentially
381+
* computing each digest.
382+
* @ctx: an optional initial context, which may have already processed data. If
383+
* NULL, a default initial context is used (equivalent to sha256_init()).
384+
* @data1: data for the first message
385+
* @data2: data for the second message
386+
* @len: the length of each of @data1 and @data2, in bytes
387+
* @out1: (output) the first SHA-256 message digest
388+
* @out2: (output) the second SHA-256 message digest
389+
*
390+
* Context: Any context.
391+
*/
392+
void sha256_finup_2x(const struct sha256_ctx *ctx, const u8 *data1,
393+
const u8 *data2, size_t len, u8 out1[SHA256_DIGEST_SIZE],
394+
u8 out2[SHA256_DIGEST_SIZE]);
395+
396+
/**
397+
* sha256_finup_2x_is_optimized() - Check if sha256_finup_2x() is using a real
398+
* interleaved implementation, as opposed to a
399+
* sequential fallback
400+
* @return: true if optimized
401+
*
402+
* Context: Any context.
403+
*/
404+
bool sha256_finup_2x_is_optimized(void);
405+
378406
/**
379407
* struct hmac_sha256_key - Prepared key for HMAC-SHA256
380408
* @key: private

0 commit comments

Comments
 (0)