[dm-devel] [PATCH v2] dm-crypt: fix deadlock when swapping to encrypted device
Mikulas Patocka
mpatocka at redhat.com
Fri Nov 27 17:54:13 UTC 2020
Hi
This is second version of the dm-crypt swapping patch.
Changes since the first version:
- we only count write bios
- when the user changes
/sys/module/dm_crypt/parameters/max_bios_in_flight, the change is
instantly applied to all dm-crypt instances
- do not lock up if the user sets max_bios_in_flight to zero
I was considering making the default value some fraction of memory, but it
didn't work - i.e. for eaxmple, if we have 8GB machine and 256MB or more
dm-crypt memory, it doesn't work (it doesn't deadlock, but it slows down
the machine very much, to the point that it's unuseable). With smaller
dm-crypt memory, it is more responsive.
Mikulas
From: Mikulas Patocka <mpatocka at redhat.com>
The system would deadlock when swapping to a dm-crypt device. The reason
is that for each incoming write bio, dm-crypt allocates memory that holds
encrypted data. These excessive allocations exhaust all the memory and the
result is either deadlock or OOM trigger.
This patch limits the number of in-flight bios, so that the memory
consumed by dm-crypt is limited. If we are over the limit, we block in the
function crypt_map, so that the caller will not attempt to send more bios.
This is similar to request-based drivers - they will also block when the
number of bios is over the limit.
Signed-off-by: Mikulas Patocka <mpatocka at redhat.com>
Cc: stable at vger.kernel.org
Index: linux-2.6/drivers/md/dm-crypt.c
===================================================================
--- linux-2.6.orig/drivers/md/dm-crypt.c
+++ linux-2.6/drivers/md/dm-crypt.c
@@ -214,16 +214,24 @@ struct crypt_config {
mempool_t page_pool;
struct bio_set bs;
+
+ int bio_limit;
+ struct semaphore bio_limit_semaphore;
+ struct mutex bio_limit_lock;
+
struct mutex bio_alloc_lock;
u8 *authenc_key; /* space for keys in authenc() format (if used) */
u8 key[];
};
+#define MAX_BIOS 4096
#define MIN_IOS 64
#define MAX_TAG_SIZE 480
#define POOL_ENTRY_SIZE 512
+static int bio_limit = MAX_BIOS;
+
static DEFINE_SPINLOCK(dm_crypt_clients_lock);
static unsigned dm_crypt_clients_n = 0;
static volatile unsigned long dm_crypt_pages_per_client;
@@ -1713,6 +1721,8 @@ static void crypt_dec_pending(struct dm_
kfree(io->integrity_metadata);
base_bio->bi_status = error;
+ if (bio_data_dir(base_bio) == WRITE)
+ up(&cc->bio_limit_semaphore);
bio_endio(base_bio);
}
@@ -2567,6 +2577,7 @@ static void crypt_dtr(struct dm_target *
kfree_sensitive(cc->cipher_auth);
kfree_sensitive(cc->authenc_key);
+ mutex_destroy(&cc->bio_limit_lock);
mutex_destroy(&cc->bio_alloc_lock);
/* Must zero key material before freeing */
@@ -3007,6 +3018,7 @@ static int crypt_ctr(struct dm_target *t
int key_size;
unsigned int align_mask;
unsigned long long tmpll;
+ int latch;
int ret;
size_t iv_size_padding, additional_req_size;
char dummy;
@@ -3106,6 +3118,12 @@ static int crypt_ctr(struct dm_target *t
goto bad;
}
+ latch = READ_ONCE(bio_limit);
+ if (unlikely(latch <= 0))
+ latch = MAX_BIOS;
+ cc->bio_limit = latch;
+ sema_init(&cc->bio_limit_semaphore, latch);
+ mutex_init(&cc->bio_limit_lock);
mutex_init(&cc->bio_alloc_lock);
ret = -EINVAL;
@@ -3234,6 +3252,25 @@ static int crypt_map(struct dm_target *t
if (unlikely(bio->bi_iter.bi_size & (cc->sector_size - 1)))
return DM_MAPIO_KILL;
+ if (bio_data_dir(bio) == WRITE) {
+ int latch = READ_ONCE(bio_limit);
+ if (unlikely(latch <= 0))
+ latch = MAX_BIOS;
+ if (unlikely(cc->bio_limit != latch)) {
+ mutex_lock(&cc->bio_limit_lock);
+ while (latch < cc->bio_limit) {
+ down(&cc->bio_limit_semaphore);
+ cc->bio_limit--;
+ }
+ while (latch > cc->bio_limit) {
+ up(&cc->bio_limit_semaphore);
+ cc->bio_limit++;
+ }
+ mutex_unlock(&cc->bio_limit_lock);
+ }
+ down(&cc->bio_limit_semaphore);
+ }
+
io = dm_per_bio_data(bio, cc->per_bio_data_size);
crypt_io_init(io, cc, bio, dm_target_offset(ti, bio->bi_iter.bi_sector));
@@ -3461,6 +3498,9 @@ static void __exit dm_crypt_exit(void)
module_init(dm_crypt_init);
module_exit(dm_crypt_exit);
+module_param_named(max_bios_in_flight, bio_limit, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_bios_in_flight, "maximum number of bios in flight");
+
MODULE_AUTHOR("Jana Saout <jana at saout.de>");
MODULE_DESCRIPTION(DM_NAME " target for transparent encryption / decryption");
MODULE_LICENSE("GPL");
More information about the dm-devel
mailing list