Skip to content

Commit 0d815e3

Browse files
author
Mikulas Patocka
committed
dm-crypt: limit the size of encryption requests
There was a performance regression reported where dm-crypt would perform worse on new kernels than on old kernels. The reason is that the old kernels split the bios to NVMe request size (that is usually 65536 or 131072 bytes) and the new kernels pass the big bios through dm-crypt and split them underneath. If a big 1MiB bio is passed to dm-crypt, dm-crypt processes it on a single core without parallelization and this is what causes the performance degradation. This commit introduces new tunable variables /sys/module/dm_crypt/parameters/max_read_size and /sys/module/dm_crypt/parameters/max_write_size that specify the maximum bio size for dm-crypt. Bios larger than this value are split, so that they can be encrypted in parallel by multiple cores. If these variables are '0', a default 131072 is used. Splitting bios may cause performance regressions in other workloads - if this happens, the user should increase the value in max_read_size and max_write_size variables. max_read_size: 128k 2399MiB/s 256k 2368MiB/s 512k 1986MiB/s 1024 1790MiB/s max_write_size: 128k 1712MiB/s 256k 1651MiB/s 512k 1537MiB/s 1024k 1332MiB/s Note that if you run dm-crypt inside a virtual machine, you may need to do "echo numa >/sys/module/workqueue/parameters/default_affinity_scope" to improve performance. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Tested-by: Laurence Oberman <loberman@redhat.com>
1 parent 6fce1f4 commit 0d815e3

2 files changed

Lines changed: 40 additions & 3 deletions

File tree

Documentation/admin-guide/device-mapper/dm-crypt.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@ iv_large_sectors
160160
The <iv_offset> must be multiple of <sector_size> (in 512 bytes units)
161161
if this flag is specified.
162162

163+
164+
Module parameters::
165+
max_read_size
166+
max_write_size
167+
Maximum size of read or write requests. When a request larger than this size
168+
is received, dm-crypt will split the request. The splitting improves
169+
concurrency (the split requests could be encrypted in parallel by multiple
170+
cores), but it also causes overhead. The user should tune these parameters to
171+
fit the actual workload.
172+
173+
163174
Example scripts
164175
===============
165176
LUKS (Linux Unified Key Setup) is now the preferred way to set up disk

drivers/md/dm-crypt.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,31 @@ static unsigned int dm_crypt_clients_n;
241241
static volatile unsigned long dm_crypt_pages_per_client;
242242
#define DM_CRYPT_MEMORY_PERCENT 2
243243
#define DM_CRYPT_MIN_PAGES_PER_CLIENT (BIO_MAX_VECS * 16)
244+
#define DM_CRYPT_DEFAULT_MAX_READ_SIZE 131072
245+
#define DM_CRYPT_DEFAULT_MAX_WRITE_SIZE 131072
246+
247+
static unsigned int max_read_size = 0;
248+
module_param(max_read_size, uint, 0644);
249+
MODULE_PARM_DESC(max_read_size, "Maximum size of a read request");
250+
static unsigned int max_write_size = 0;
251+
module_param(max_write_size, uint, 0644);
252+
MODULE_PARM_DESC(max_write_size, "Maximum size of a write request");
253+
static unsigned get_max_request_size(struct crypt_config *cc, bool wrt)
254+
{
255+
unsigned val, sector_align;
256+
val = !wrt ? READ_ONCE(max_read_size) : READ_ONCE(max_write_size);
257+
if (likely(!val))
258+
val = !wrt ? DM_CRYPT_DEFAULT_MAX_READ_SIZE : DM_CRYPT_DEFAULT_MAX_WRITE_SIZE;
259+
if (wrt || cc->on_disk_tag_size) {
260+
if (unlikely(val > BIO_MAX_VECS << PAGE_SHIFT))
261+
val = BIO_MAX_VECS << PAGE_SHIFT;
262+
}
263+
sector_align = max(bdev_logical_block_size(cc->dev->bdev), (unsigned)cc->sector_size);
264+
val = round_down(val, sector_align);
265+
if (unlikely(!val))
266+
val = sector_align;
267+
return val >> SECTOR_SHIFT;
268+
}
244269

245270
static void crypt_endio(struct bio *clone);
246271
static void kcryptd_queue_crypt(struct dm_crypt_io *io);
@@ -3474,6 +3499,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio)
34743499
{
34753500
struct dm_crypt_io *io;
34763501
struct crypt_config *cc = ti->private;
3502+
unsigned max_sectors;
34773503

34783504
/*
34793505
* If bio is REQ_PREFLUSH or REQ_OP_DISCARD, just bypass crypt queues.
@@ -3492,9 +3518,9 @@ static int crypt_map(struct dm_target *ti, struct bio *bio)
34923518
/*
34933519
* Check if bio is too large, split as needed.
34943520
*/
3495-
if (unlikely(bio->bi_iter.bi_size > (BIO_MAX_VECS << PAGE_SHIFT)) &&
3496-
(bio_data_dir(bio) == WRITE || cc->on_disk_tag_size))
3497-
dm_accept_partial_bio(bio, ((BIO_MAX_VECS << PAGE_SHIFT) >> SECTOR_SHIFT));
3521+
max_sectors = get_max_request_size(cc, bio_data_dir(bio) == WRITE);
3522+
if (unlikely(bio_sectors(bio) > max_sectors))
3523+
dm_accept_partial_bio(bio, max_sectors);
34983524

34993525
/*
35003526
* Ensure that bio is a multiple of internal sector encryption size

0 commit comments

Comments
 (0)