diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2015-02-13 08:24:41 -0500 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2015-02-16 11:11:13 -0500 |
commit | 7145c241a1bf2841952c3e297c4080b357b3e52d (patch) | |
tree | 12c9a8ffce670390ea5b518fbe6d9b64f90f05b9 | |
parent | cf2f1abfbd0dba701f7f16ef619e4d2485de3366 (diff) |
dm crypt: avoid deadlock in mempools
Fix a theoretical deadlock introduced in the previous commit ("dm crypt:
don't allocate pages for a partial request").
The function crypt_alloc_buffer may be called concurrently. If we allocate
from the mempool concurrently, there is a possibility of deadlock. For
example, if we have mempool of 256 pages, two processes, each wanting
256, pages allocate from the mempool concurrently, it may deadlock in a
situation where both processes have allocated 128 pages and the mempool
is exhausted.
To avoid such a scenario we allocate the pages under a mutex. In order
to not degrade performance with excessive locking, we try non-blocking
allocations without a mutex first and if that fails, we fallback to a
blocking allocations with a mutex.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r-- | drivers/md/dm-crypt.c | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 6199245ea6a6..fa1dba1d06f7 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c | |||
@@ -124,6 +124,7 @@ struct crypt_config { | |||
124 | mempool_t *req_pool; | 124 | mempool_t *req_pool; |
125 | mempool_t *page_pool; | 125 | mempool_t *page_pool; |
126 | struct bio_set *bs; | 126 | struct bio_set *bs; |
127 | struct mutex bio_alloc_lock; | ||
127 | 128 | ||
128 | struct workqueue_struct *io_queue; | 129 | struct workqueue_struct *io_queue; |
129 | struct workqueue_struct *crypt_queue; | 130 | struct workqueue_struct *crypt_queue; |
@@ -949,27 +950,51 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone); | |||
949 | /* | 950 | /* |
950 | * Generate a new unfragmented bio with the given size | 951 | * Generate a new unfragmented bio with the given size |
951 | * This should never violate the device limitations | 952 | * This should never violate the device limitations |
953 | * | ||
954 | * This function may be called concurrently. If we allocate from the mempool | ||
955 | * concurrently, there is a possibility of deadlock. For example, if we have | ||
956 | * mempool of 256 pages, two processes, each wanting 256, pages allocate from | ||
957 | * the mempool concurrently, it may deadlock in a situation where both processes | ||
958 | * have allocated 128 pages and the mempool is exhausted. | ||
959 | * | ||
960 | * In order to avoid this scenario we allocate the pages under a mutex. | ||
961 | * | ||
962 | * In order to not degrade performance with excessive locking, we try | ||
963 | * non-blocking allocations without a mutex first but on failure we fallback | ||
964 | * to blocking allocations with a mutex. | ||
952 | */ | 965 | */ |
953 | static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) | 966 | static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) |
954 | { | 967 | { |
955 | struct crypt_config *cc = io->cc; | 968 | struct crypt_config *cc = io->cc; |
956 | struct bio *clone; | 969 | struct bio *clone; |
957 | unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; | 970 | unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
958 | gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; | 971 | gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; |
959 | unsigned i, len; | 972 | unsigned i, len, remaining_size; |
960 | struct page *page; | 973 | struct page *page; |
961 | struct bio_vec *bvec; | 974 | struct bio_vec *bvec; |
962 | 975 | ||
976 | retry: | ||
977 | if (unlikely(gfp_mask & __GFP_WAIT)) | ||
978 | mutex_lock(&cc->bio_alloc_lock); | ||
979 | |||
963 | clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); | 980 | clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); |
964 | if (!clone) | 981 | if (!clone) |
965 | return NULL; | 982 | goto return_clone; |
966 | 983 | ||
967 | clone_init(io, clone); | 984 | clone_init(io, clone); |
968 | 985 | ||
986 | remaining_size = size; | ||
987 | |||
969 | for (i = 0; i < nr_iovecs; i++) { | 988 | for (i = 0; i < nr_iovecs; i++) { |
970 | page = mempool_alloc(cc->page_pool, gfp_mask); | 989 | page = mempool_alloc(cc->page_pool, gfp_mask); |
990 | if (!page) { | ||
991 | crypt_free_buffer_pages(cc, clone); | ||
992 | bio_put(clone); | ||
993 | gfp_mask |= __GFP_WAIT; | ||
994 | goto retry; | ||
995 | } | ||
971 | 996 | ||
972 | len = (size > PAGE_SIZE) ? PAGE_SIZE : size; | 997 | len = (remaining_size > PAGE_SIZE) ? PAGE_SIZE : remaining_size; |
973 | 998 | ||
974 | bvec = &clone->bi_io_vec[clone->bi_vcnt++]; | 999 | bvec = &clone->bi_io_vec[clone->bi_vcnt++]; |
975 | bvec->bv_page = page; | 1000 | bvec->bv_page = page; |
@@ -978,9 +1003,13 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) | |||
978 | 1003 | ||
979 | clone->bi_iter.bi_size += len; | 1004 | clone->bi_iter.bi_size += len; |
980 | 1005 | ||
981 | size -= len; | 1006 | remaining_size -= len; |
982 | } | 1007 | } |
983 | 1008 | ||
1009 | return_clone: | ||
1010 | if (unlikely(gfp_mask & __GFP_WAIT)) | ||
1011 | mutex_unlock(&cc->bio_alloc_lock); | ||
1012 | |||
984 | return clone; | 1013 | return clone; |
985 | } | 1014 | } |
986 | 1015 | ||
@@ -1679,6 +1708,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
1679 | goto bad; | 1708 | goto bad; |
1680 | } | 1709 | } |
1681 | 1710 | ||
1711 | mutex_init(&cc->bio_alloc_lock); | ||
1712 | |||
1682 | ret = -EINVAL; | 1713 | ret = -EINVAL; |
1683 | if (sscanf(argv[2], "%llu%c", &tmpll, &dummy) != 1) { | 1714 | if (sscanf(argv[2], "%llu%c", &tmpll, &dummy) != 1) { |
1684 | ti->error = "Invalid iv_offset sector"; | 1715 | ti->error = "Invalid iv_offset sector"; |