Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5721d4e

Browse files
nhukcMike Snitzer
authored andcommitted
dm verity: Add optional "try_verify_in_tasklet" feature
Using tasklets for disk verification can reduce IO latency. When there are accelerated hash instructions it is often better to compute the hash immediately using a tasklet rather than deferring verification to a work-queue. This reduces time spent waiting to schedule work-queue jobs, but requires spending slightly more time in interrupt context. If the dm-bufio cache does not have the required hashes we fallback to the work-queue implementation. FEC is only possible using work-queue because code to support the FEC feature may sleep. The following shows a speed comparison of random reads on a dm-verity device. The dm-verity device uses a 1G ramdisk for data and a 1G ramdisk for hashes. One test was run using tasklets and one test was run using the existing work-queue solution. Both tests were run when the dm-bufio cache was hot. The tasklet implementation performs significantly better since there is no time spent waiting for work-queue jobs to be scheduled. READ: bw=181MiB/s (190MB/s), 181MiB/s-181MiB/s (190MB/s-190MB/s), io=512MiB (537MB), run=2827-2827msec READ: bw=23.6MiB/s (24.8MB/s), 23.6MiB/s-23.6MiB/s (24.8MB/s-24.8MB/s), io=512MiB (537MB), run=21688-21688msec Signed-off-by: Nathan Huckleberry <[email protected]> Signed-off-by: Mike Snitzer <[email protected]>
1 parent b32d458 commit 5721d4e

File tree

2 files changed

+91
-18
lines changed

2 files changed

+91
-18
lines changed

drivers/md/dm-verity-target.c

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#define DM_VERITY_OPT_PANIC "panic_on_corruption"
3535
#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
3636
#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
37+
#define DM_VERITY_OPT_TASKLET_VERIFY "try_verify_in_tasklet"
3738

3839
#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC + \
3940
DM_VERITY_ROOT_HASH_VERIFICATION_OPTS)
@@ -220,7 +221,7 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
220221
struct mapped_device *md = dm_table_get_md(v->ti->table);
221222

222223
/* Corruption should be visible in device status in all modes */
223-
v->hash_failed = 1;
224+
v->hash_failed = true;
224225

225226
if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
226227
goto out;
@@ -286,7 +287,19 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
286287

287288
verity_hash_at_level(v, block, level, &hash_block, &offset);
288289

289-
data = dm_bufio_read(v->bufio, hash_block, &buf);
290+
if (io->in_tasklet) {
291+
data = dm_bufio_get(v->bufio, hash_block, &buf);
292+
if (data == NULL) {
293+
/*
294+
* In tasklet and the hash was not in the bufio cache.
295+
* Return early and resume execution from a work-queue
296+
* to read the hash from disk.
297+
*/
298+
return -EAGAIN;
299+
}
300+
} else
301+
data = dm_bufio_read(v->bufio, hash_block, &buf);
302+
290303
if (IS_ERR(data))
291304
return PTR_ERR(data);
292305

@@ -307,6 +320,14 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
307320
if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
308321
v->digest_size) == 0))
309322
aux->hash_verified = 1;
323+
else if (io->in_tasklet) {
324+
/*
325+
* Error handling code (FEC included) cannot be run in a
326+
* tasklet since it may sleep, so fallback to work-queue.
327+
*/
328+
r = -EAGAIN;
329+
goto release_ret_r;
330+
}
310331
else if (verity_fec_decode(v, io,
311332
DM_VERITY_BLOCK_TYPE_METADATA,
312333
hash_block, data, NULL) == 0)
@@ -474,9 +495,14 @@ static int verity_verify_io(struct dm_verity_io *io)
474495
bool is_zero;
475496
struct dm_verity *v = io->v;
476497
struct bvec_iter start;
477-
unsigned b;
498+
/*
499+
* Copy the iterator in case we need to restart verification in a
500+
* work-queue.
501+
*/
502+
struct bvec_iter iter_copy = io->iter;
478503
struct crypto_wait wait;
479504
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
505+
unsigned int b;
480506

481507
for (b = 0; b < io->n_blocks; b++) {
482508
int r;
@@ -485,7 +511,7 @@ static int verity_verify_io(struct dm_verity_io *io)
485511

486512
if (v->validated_blocks &&
487513
likely(test_bit(cur_block, v->validated_blocks))) {
488-
verity_bv_skip_block(v, io, &io->iter);
514+
verity_bv_skip_block(v, io, &iter_copy);
489515
continue;
490516
}
491517

@@ -500,7 +526,7 @@ static int verity_verify_io(struct dm_verity_io *io)
500526
* If we expect a zero block, don't validate, just
501527
* return zeros.
502528
*/
503-
r = verity_for_bv_block(v, io, &io->iter,
529+
r = verity_for_bv_block(v, io, &iter_copy,
504530
verity_bv_zero);
505531
if (unlikely(r < 0))
506532
return r;
@@ -512,8 +538,8 @@ static int verity_verify_io(struct dm_verity_io *io)
512538
if (unlikely(r < 0))
513539
return r;
514540

515-
start = io->iter;
516-
r = verity_for_io_block(v, io, &io->iter, &wait);
541+
start = iter_copy;
542+
r = verity_for_io_block(v, io, &iter_copy, &wait);
517543
if (unlikely(r < 0))
518544
return r;
519545

@@ -527,8 +553,14 @@ static int verity_verify_io(struct dm_verity_io *io)
527553
if (v->validated_blocks)
528554
set_bit(cur_block, v->validated_blocks);
529555
continue;
556+
} else if (io->in_tasklet) {
557+
/*
558+
* Error handling code (FEC included) cannot be run in a
559+
* tasklet since it may sleep, so fallback to work-queue.
560+
*/
561+
return -EAGAIN;
530562
} else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
531-
cur_block, NULL, &start) == 0) {
563+
cur_block, NULL, &start) == 0) {
532564
continue;
533565
} else {
534566
if (bio->bi_status) {
@@ -566,7 +598,8 @@ static void verity_finish_io(struct dm_verity_io *io, blk_status_t status)
566598
bio->bi_end_io = io->orig_bi_end_io;
567599
bio->bi_status = status;
568600

569-
verity_fec_finish_io(io);
601+
if (!io->in_tasklet)
602+
verity_fec_finish_io(io);
570603

571604
bio_endio(bio);
572605
}
@@ -575,9 +608,29 @@ static void verity_work(struct work_struct *w)
575608
{
576609
struct dm_verity_io *io = container_of(w, struct dm_verity_io, work);
577610

611+
io->in_tasklet = false;
612+
613+
verity_fec_init_io(io);
578614
verity_finish_io(io, errno_to_blk_status(verity_verify_io(io)));
579615
}
580616

617+
static void verity_tasklet(unsigned long data)
618+
{
619+
struct dm_verity_io *io = (struct dm_verity_io *)data;
620+
int err;
621+
622+
io->in_tasklet = true;
623+
err = verity_verify_io(io);
624+
if (err == -EAGAIN) {
625+
/* fallback to retrying with work-queue */
626+
INIT_WORK(&io->work, verity_work);
627+
queue_work(io->v->verify_wq, &io->work);
628+
return;
629+
}
630+
631+
verity_finish_io(io, errno_to_blk_status(err));
632+
}
633+
581634
static void verity_end_io(struct bio *bio)
582635
{
583636
struct dm_verity_io *io = bio->bi_private;
@@ -588,8 +641,13 @@ static void verity_end_io(struct bio *bio)
588641
return;
589642
}
590643

591-
INIT_WORK(&io->work, verity_work);
592-
queue_work(io->v->verify_wq, &io->work);
644+
if (io->v->use_tasklet) {
645+
tasklet_init(&io->tasklet, verity_tasklet, (unsigned long)io);
646+
tasklet_schedule(&io->tasklet);
647+
} else {
648+
INIT_WORK(&io->work, verity_work);
649+
queue_work(io->v->verify_wq, &io->work);
650+
}
593651
}
594652

595653
/*
@@ -700,8 +758,6 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
700758
bio->bi_private = io;
701759
io->iter = bio->bi_iter;
702760

703-
verity_fec_init_io(io);
704-
705761
verity_submit_prefetch(v, io);
706762

707763
submit_bio_noacct(bio);
@@ -751,6 +807,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
751807
args++;
752808
if (v->validated_blocks)
753809
args++;
810+
if (v->use_tasklet)
811+
args++;
754812
if (v->signature_key_desc)
755813
args += DM_VERITY_ROOT_HASH_VERIFICATION_OPTS;
756814
if (!args)
@@ -776,6 +834,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
776834
DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
777835
if (v->validated_blocks)
778836
DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
837+
if (v->use_tasklet)
838+
DMEMIT(" " DM_VERITY_OPT_TASKLET_VERIFY);
779839
sz = verity_fec_status_table(v, sz, result, maxlen);
780840
if (v->signature_key_desc)
781841
DMEMIT(" " DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY
@@ -1011,19 +1071,23 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
10111071
return r;
10121072
continue;
10131073

1074+
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) {
1075+
v->use_tasklet = true;
1076+
continue;
1077+
10141078
} else if (verity_is_fec_opt_arg(arg_name)) {
10151079
r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
10161080
if (r)
10171081
return r;
10181082
continue;
1083+
10191084
} else if (verity_verify_is_sig_opt_arg(arg_name)) {
10201085
r = verity_verify_sig_parse_opt_args(as, v,
10211086
verify_args,
10221087
&argc, arg_name);
10231088
if (r)
10241089
return r;
10251090
continue;
1026-
10271091
}
10281092

10291093
ti->error = "Unrecognized verity feature request";
@@ -1155,7 +1219,11 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
11551219
goto bad;
11561220
}
11571221

1158-
v->tfm = crypto_alloc_ahash(v->alg_name, 0, 0);
1222+
/*
1223+
* FIXME: CRYPTO_ALG_ASYNC should be conditional on v->use_tasklet
1224+
* but verity_parse_opt_args() happens below and has data dep on tfm.
1225+
*/
1226+
v->tfm = crypto_alloc_ahash(v->alg_name, 0, CRYPTO_ALG_ASYNC);
11591227
if (IS_ERR(v->tfm)) {
11601228
ti->error = "Cannot initialize hash function";
11611229
r = PTR_ERR(v->tfm);
@@ -1265,7 +1333,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
12651333

12661334
v->bufio = dm_bufio_client_create(v->hash_dev->bdev,
12671335
1 << v->hash_dev_block_bits, 1, sizeof(struct buffer_aux),
1268-
dm_bufio_alloc_callback, NULL, 0);
1336+
dm_bufio_alloc_callback, NULL,
1337+
v->use_tasklet ? DM_BUFIO_CLIENT_NO_SLEEP : 0);
12691338
if (IS_ERR(v->bufio)) {
12701339
ti->error = "Cannot initialize dm-bufio";
12711340
r = PTR_ERR(v->bufio);
@@ -1312,7 +1381,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
13121381
static struct target_type verity_target = {
13131382
.name = "verity",
13141383
.features = DM_TARGET_IMMUTABLE,
1315-
.version = {1, 8, 0},
1384+
.version = {1, 9, 0},
13161385
.module = THIS_MODULE,
13171386
.ctr = verity_ctr,
13181387
.dtr = verity_dtr,

drivers/md/dm-verity.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <linux/dm-bufio.h>
1515
#include <linux/device-mapper.h>
16+
#include <linux/interrupt.h>
1617
#include <crypto/hash.h>
1718

1819
#define DM_VERITY_MAX_LEVELS 63
@@ -51,9 +52,10 @@ struct dm_verity {
5152
unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
5253
unsigned char levels; /* the number of tree levels */
5354
unsigned char version;
55+
bool hash_failed:1; /* set if hash of any block failed */
56+
bool use_tasklet:1; /* try to verify in tasklet before work-queue */
5457
unsigned digest_size; /* digest size for the current hash algorithm */
5558
unsigned int ahash_reqsize;/* the size of temporary space for crypto */
56-
int hash_failed; /* set to 1 if hash of any block failed */
5759
enum verity_mode mode; /* mode for handling verification errors */
5860
unsigned corrupted_errs;/* Number of errors for corrupted blocks */
5961

@@ -76,10 +78,12 @@ struct dm_verity_io {
7678

7779
sector_t block;
7880
unsigned n_blocks;
81+
bool in_tasklet;
7982

8083
struct bvec_iter iter;
8184

8285
struct work_struct work;
86+
struct tasklet_struct tasklet;
8387

8488
/*
8589
* Three variably-size fields follow this struct:

0 commit comments

Comments
 (0)