Skip to content

Commit 39d42fa

Browse files
ignatksnitm
authored andcommitted
dm crypt: add flags to optionally bypass kcryptd workqueues
This is a follow up to [1] that detailed latency problems associated with dm-crypt's use of workqueues when processing IO. Current dm-crypt implementation creates a significant IO performance overhead (at least on small IO block sizes) for both latency and throughput. We suspect offloading IO request processing into workqueues and async threads is more harmful these days with the modern fast storage. I also did some digging into the dm-crypt git history and much of this async processing is not needed anymore, because the reasons it was added are mostly gone from the kernel. More details can be found in [2] (see "Git archeology" section). This change adds DM_CRYPT_NO_READ_WORKQUEUE and DM_CRYPT_NO_WRITE_WORKQUEUE flags for read and write BIOs, which direct dm-crypt to not offload crypto operations into kcryptd workqueues. In addition, writes are not buffered to be sorted in the dm-crypt red-black tree, but dispatched immediately. For cases, where crypto operations cannot happen (hard interrupt context, for example the read path of some NVME drivers), we offload the work to a tasklet rather than a workqueue. These flags only ensure no async BIO processing in the dm-crypt module. It is worth noting that some Crypto API implementations may offload encryption into their own workqueues, which are independent of the dm-crypt and its configuration. However upon enabling these new flags dm-crypt will instruct Crypto API not to backlog crypto requests. To give an idea of the performance gains for certain workloads, consider the script, and results when tested against various devices, detailed here: https://www.redhat.com/archives/dm-devel/2020-July/msg00138.html [1]: https://www.spinics.net/lists/dm-crypt/msg07516.html [2]: https://blog.cloudflare.com/speeding-up-linux-disk-encryption/ Signed-off-by: Ignat Korchagin <[email protected]> Reviewed-by: Damien Le Moal <[email protected]> Reviewed-by: Bob Liu <[email protected]> Signed-off-by: Mike Snitzer <[email protected]>
1 parent 70704c3 commit 39d42fa

File tree

1 file changed

+42
-8
lines changed

1 file changed

+42
-8
lines changed

drivers/md/dm-crypt.c

+42-8
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct dm_crypt_io {
6969
u8 *integrity_metadata;
7070
bool integrity_metadata_from_pool;
7171
struct work_struct work;
72+
struct tasklet_struct tasklet;
7273

7374
struct convert_context ctx;
7475

@@ -127,7 +128,8 @@ struct iv_elephant_private {
127128
* and encrypts / decrypts at the same time.
128129
*/
129130
enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
130-
DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
131+
DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
132+
DM_CRYPT_NO_READ_WORKQUEUE, DM_CRYPT_NO_WRITE_WORKQUEUE };
131133

132134
enum cipher_flags {
133135
CRYPT_MODE_INTEGRITY_AEAD, /* Use authenticated mode for cihper */
@@ -1523,7 +1525,7 @@ static void crypt_free_req(struct crypt_config *cc, void *req, struct bio *base_
15231525
* Encrypt / decrypt data from one bio to another one (can be the same one)
15241526
*/
15251527
static blk_status_t crypt_convert(struct crypt_config *cc,
1526-
struct convert_context *ctx)
1528+
struct convert_context *ctx, bool atomic)
15271529
{
15281530
unsigned int tag_offset = 0;
15291531
unsigned int sector_step = cc->sector_size >> SECTOR_SHIFT;
@@ -1566,7 +1568,8 @@ static blk_status_t crypt_convert(struct crypt_config *cc,
15661568
atomic_dec(&ctx->cc_pending);
15671569
ctx->cc_sector += sector_step;
15681570
tag_offset++;
1569-
cond_resched();
1571+
if (!atomic)
1572+
cond_resched();
15701573
continue;
15711574
/*
15721575
* There was a data integrity error.
@@ -1892,7 +1895,8 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async)
18921895

18931896
clone->bi_iter.bi_sector = cc->start + io->sector;
18941897

1895-
if (likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) {
1898+
if ((likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) ||
1899+
test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags)) {
18961900
generic_make_request(clone);
18971901
return;
18981902
}
@@ -1941,7 +1945,8 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
19411945
sector += bio_sectors(clone);
19421946

19431947
crypt_inc_pending(io);
1944-
r = crypt_convert(cc, &io->ctx);
1948+
r = crypt_convert(cc, &io->ctx,
1949+
test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags));
19451950
if (r)
19461951
io->error = r;
19471952
crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending);
@@ -1971,7 +1976,8 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
19711976
crypt_convert_init(cc, &io->ctx, io->base_bio, io->base_bio,
19721977
io->sector);
19731978

1974-
r = crypt_convert(cc, &io->ctx);
1979+
r = crypt_convert(cc, &io->ctx,
1980+
test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags));
19751981
if (r)
19761982
io->error = r;
19771983

@@ -2031,10 +2037,28 @@ static void kcryptd_crypt(struct work_struct *work)
20312037
kcryptd_crypt_write_convert(io);
20322038
}
20332039

2040+
static void kcryptd_crypt_tasklet(unsigned long work)
2041+
{
2042+
kcryptd_crypt((struct work_struct *)work);
2043+
}
2044+
20342045
static void kcryptd_queue_crypt(struct dm_crypt_io *io)
20352046
{
20362047
struct crypt_config *cc = io->cc;
20372048

2049+
if ((bio_data_dir(io->base_bio) == READ && test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags)) ||
2050+
(bio_data_dir(io->base_bio) == WRITE && test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags))) {
2051+
if (in_irq()) {
2052+
/* Crypto API's "skcipher_walk_first() refuses to work in hard IRQ context */
2053+
tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work);
2054+
tasklet_schedule(&io->tasklet);
2055+
return;
2056+
}
2057+
2058+
kcryptd_crypt(&io->work);
2059+
return;
2060+
}
2061+
20382062
INIT_WORK(&io->work, kcryptd_crypt);
20392063
queue_work(cc->crypt_queue, &io->work);
20402064
}
@@ -2838,7 +2862,7 @@ static int crypt_ctr_optional(struct dm_target *ti, unsigned int argc, char **ar
28382862
struct crypt_config *cc = ti->private;
28392863
struct dm_arg_set as;
28402864
static const struct dm_arg _args[] = {
2841-
{0, 6, "Invalid number of feature args"},
2865+
{0, 8, "Invalid number of feature args"},
28422866
};
28432867
unsigned int opt_params, val;
28442868
const char *opt_string, *sval;
@@ -2868,6 +2892,10 @@ static int crypt_ctr_optional(struct dm_target *ti, unsigned int argc, char **ar
28682892

28692893
else if (!strcasecmp(opt_string, "submit_from_crypt_cpus"))
28702894
set_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags);
2895+
else if (!strcasecmp(opt_string, "no_read_workqueue"))
2896+
set_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags);
2897+
else if (!strcasecmp(opt_string, "no_write_workqueue"))
2898+
set_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags);
28712899
else if (sscanf(opt_string, "integrity:%u:", &val) == 1) {
28722900
if (val == 0 || val > MAX_TAG_SIZE) {
28732901
ti->error = "Invalid integrity arguments";
@@ -3196,6 +3224,8 @@ static void crypt_status(struct dm_target *ti, status_type_t type,
31963224
num_feature_args += !!ti->num_discard_bios;
31973225
num_feature_args += test_bit(DM_CRYPT_SAME_CPU, &cc->flags);
31983226
num_feature_args += test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags);
3227+
num_feature_args += test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags);
3228+
num_feature_args += test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags);
31993229
num_feature_args += cc->sector_size != (1 << SECTOR_SHIFT);
32003230
num_feature_args += test_bit(CRYPT_IV_LARGE_SECTORS, &cc->cipher_flags);
32013231
if (cc->on_disk_tag_size)
@@ -3208,6 +3238,10 @@ static void crypt_status(struct dm_target *ti, status_type_t type,
32083238
DMEMIT(" same_cpu_crypt");
32093239
if (test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags))
32103240
DMEMIT(" submit_from_crypt_cpus");
3241+
if (test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags))
3242+
DMEMIT(" no_read_workqueue");
3243+
if (test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags))
3244+
DMEMIT(" no_write_workqueue");
32113245
if (cc->on_disk_tag_size)
32123246
DMEMIT(" integrity:%u:%s", cc->on_disk_tag_size, cc->cipher_auth);
32133247
if (cc->sector_size != (1 << SECTOR_SHIFT))
@@ -3320,7 +3354,7 @@ static void crypt_io_hints(struct dm_target *ti, struct queue_limits *limits)
33203354

33213355
static struct target_type crypt_target = {
33223356
.name = "crypt",
3323-
.version = {1, 21, 0},
3357+
.version = {1, 22, 0},
33243358
.module = THIS_MODULE,
33253359
.ctr = crypt_ctr,
33263360
.dtr = crypt_dtr,

0 commit comments

Comments
 (0)