diff options
| author | Mikulas Patocka <mpatocka@redhat.com> | 2015-02-13 08:25:59 -0500 |
|---|---|---|
| committer | Mike Snitzer <snitzer@redhat.com> | 2015-02-16 11:11:14 -0500 |
| commit | dc2676210c425ee8e5cb1bec5bc84d004ddf4179 (patch) | |
| tree | 2d38e1d74895e7cf419a0408805ed73673d69425 /drivers/md | |
| parent | 94f5e0243c48aa01441c987743dc468e2d6eaca2 (diff) | |
dm crypt: offload writes to thread
Submitting write bios directly in the encryption thread caused serious
performance degradation. On a multiprocessor machine, encryption requests
finish in a different order than they were submitted. Consequently, write
requests would be submitted in a different order and it could cause severe
performance degradation.
Move the submission of write requests to a separate thread so that the
requests can be sorted before submitting. But this commit improves
dm-crypt performance even without having dm-crypt perform request
sorting (in particular it enables IO schedulers like CFQ to sort more
effectively).
Note: it is required that a previous commit ("dm crypt: don't allocate
pages for a partial request") be applied before applying this patch.
Otherwise, this commit could introduce a crash.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md')
| -rw-r--r-- | drivers/md/dm-crypt.c | 114 |
1 files changed, 94 insertions, 20 deletions
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index c29daf417aaf..8c0e36b1d0ed 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
| 19 | #include <linux/crypto.h> | 19 | #include <linux/crypto.h> |
| 20 | #include <linux/workqueue.h> | 20 | #include <linux/workqueue.h> |
| 21 | #include <linux/kthread.h> | ||
| 21 | #include <linux/backing-dev.h> | 22 | #include <linux/backing-dev.h> |
| 22 | #include <linux/atomic.h> | 23 | #include <linux/atomic.h> |
| 23 | #include <linux/scatterlist.h> | 24 | #include <linux/scatterlist.h> |
| @@ -58,6 +59,8 @@ struct dm_crypt_io { | |||
| 58 | atomic_t io_pending; | 59 | atomic_t io_pending; |
| 59 | int error; | 60 | int error; |
| 60 | sector_t sector; | 61 | sector_t sector; |
| 62 | |||
| 63 | struct list_head list; | ||
| 61 | } CRYPTO_MINALIGN_ATTR; | 64 | } CRYPTO_MINALIGN_ATTR; |
| 62 | 65 | ||
| 63 | struct dm_crypt_request { | 66 | struct dm_crypt_request { |
| @@ -128,6 +131,10 @@ struct crypt_config { | |||
| 128 | struct workqueue_struct *io_queue; | 131 | struct workqueue_struct *io_queue; |
| 129 | struct workqueue_struct *crypt_queue; | 132 | struct workqueue_struct *crypt_queue; |
| 130 | 133 | ||
| 134 | struct task_struct *write_thread; | ||
| 135 | wait_queue_head_t write_thread_wait; | ||
| 136 | struct list_head write_thread_list; | ||
| 137 | |||
| 131 | char *cipher; | 138 | char *cipher; |
| 132 | char *cipher_string; | 139 | char *cipher_string; |
| 133 | 140 | ||
| @@ -1136,37 +1143,89 @@ static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp) | |||
| 1136 | return 0; | 1143 | return 0; |
| 1137 | } | 1144 | } |
| 1138 | 1145 | ||
| 1146 | static void kcryptd_io_read_work(struct work_struct *work) | ||
| 1147 | { | ||
| 1148 | struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work); | ||
| 1149 | |||
| 1150 | crypt_inc_pending(io); | ||
| 1151 | if (kcryptd_io_read(io, GFP_NOIO)) | ||
| 1152 | io->error = -ENOMEM; | ||
| 1153 | crypt_dec_pending(io); | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | static void kcryptd_queue_read(struct dm_crypt_io *io) | ||
| 1157 | { | ||
| 1158 | struct crypt_config *cc = io->cc; | ||
| 1159 | |||
| 1160 | INIT_WORK(&io->work, kcryptd_io_read_work); | ||
| 1161 | queue_work(cc->io_queue, &io->work); | ||
| 1162 | } | ||
| 1163 | |||
| 1139 | static void kcryptd_io_write(struct dm_crypt_io *io) | 1164 | static void kcryptd_io_write(struct dm_crypt_io *io) |
| 1140 | { | 1165 | { |
| 1141 | struct bio *clone = io->ctx.bio_out; | 1166 | struct bio *clone = io->ctx.bio_out; |
| 1167 | |||
| 1142 | generic_make_request(clone); | 1168 | generic_make_request(clone); |
| 1143 | } | 1169 | } |
| 1144 | 1170 | ||
| 1145 | static void kcryptd_io(struct work_struct *work) | 1171 | static int dmcrypt_write(void *data) |
| 1146 | { | 1172 | { |
| 1147 | struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work); | 1173 | struct crypt_config *cc = data; |
| 1174 | while (1) { | ||
| 1175 | struct list_head local_list; | ||
| 1176 | struct blk_plug plug; | ||
| 1148 | 1177 | ||
| 1149 | if (bio_data_dir(io->base_bio) == READ) { | 1178 | DECLARE_WAITQUEUE(wait, current); |
| 1150 | crypt_inc_pending(io); | ||
| 1151 | if (kcryptd_io_read(io, GFP_NOIO)) | ||
| 1152 | io->error = -ENOMEM; | ||
| 1153 | crypt_dec_pending(io); | ||
| 1154 | } else | ||
| 1155 | kcryptd_io_write(io); | ||
| 1156 | } | ||
| 1157 | 1179 | ||
| 1158 | static void kcryptd_queue_io(struct dm_crypt_io *io) | 1180 | spin_lock_irq(&cc->write_thread_wait.lock); |
| 1159 | { | 1181 | continue_locked: |
| 1160 | struct crypt_config *cc = io->cc; | ||
| 1161 | 1182 | ||
| 1162 | INIT_WORK(&io->work, kcryptd_io); | 1183 | if (!list_empty(&cc->write_thread_list)) |
| 1163 | queue_work(cc->io_queue, &io->work); | 1184 | goto pop_from_list; |
| 1185 | |||
| 1186 | __set_current_state(TASK_INTERRUPTIBLE); | ||
| 1187 | __add_wait_queue(&cc->write_thread_wait, &wait); | ||
| 1188 | |||
| 1189 | spin_unlock_irq(&cc->write_thread_wait.lock); | ||
| 1190 | |||
| 1191 | if (unlikely(kthread_should_stop())) { | ||
| 1192 | set_task_state(current, TASK_RUNNING); | ||
| 1193 | remove_wait_queue(&cc->write_thread_wait, &wait); | ||
| 1194 | break; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | schedule(); | ||
| 1198 | |||
| 1199 | set_task_state(current, TASK_RUNNING); | ||
| 1200 | spin_lock_irq(&cc->write_thread_wait.lock); | ||
| 1201 | __remove_wait_queue(&cc->write_thread_wait, &wait); | ||
| 1202 | goto continue_locked; | ||
| 1203 | |||
| 1204 | pop_from_list: | ||
| 1205 | local_list = cc->write_thread_list; | ||
| 1206 | local_list.next->prev = &local_list; | ||
| 1207 | local_list.prev->next = &local_list; | ||
| 1208 | INIT_LIST_HEAD(&cc->write_thread_list); | ||
| 1209 | |||
| 1210 | spin_unlock_irq(&cc->write_thread_wait.lock); | ||
| 1211 | |||
| 1212 | blk_start_plug(&plug); | ||
| 1213 | do { | ||
| 1214 | struct dm_crypt_io *io = container_of(local_list.next, | ||
| 1215 | struct dm_crypt_io, list); | ||
| 1216 | list_del(&io->list); | ||
| 1217 | kcryptd_io_write(io); | ||
| 1218 | } while (!list_empty(&local_list)); | ||
| 1219 | blk_finish_plug(&plug); | ||
| 1220 | } | ||
| 1221 | return 0; | ||
| 1164 | } | 1222 | } |
| 1165 | 1223 | ||
| 1166 | static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) | 1224 | static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) |
| 1167 | { | 1225 | { |
| 1168 | struct bio *clone = io->ctx.bio_out; | 1226 | struct bio *clone = io->ctx.bio_out; |
| 1169 | struct crypt_config *cc = io->cc; | 1227 | struct crypt_config *cc = io->cc; |
| 1228 | unsigned long flags; | ||
| 1170 | 1229 | ||
| 1171 | if (unlikely(io->error < 0)) { | 1230 | if (unlikely(io->error < 0)) { |
| 1172 | crypt_free_buffer_pages(cc, clone); | 1231 | crypt_free_buffer_pages(cc, clone); |
| @@ -1180,10 +1239,10 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) | |||
| 1180 | 1239 | ||
| 1181 | clone->bi_iter.bi_sector = cc->start + io->sector; | 1240 | clone->bi_iter.bi_sector = cc->start + io->sector; |
| 1182 | 1241 | ||
| 1183 | if (async) | 1242 | spin_lock_irqsave(&cc->write_thread_wait.lock, flags); |
| 1184 | kcryptd_queue_io(io); | 1243 | list_add_tail(&io->list, &cc->write_thread_list); |
| 1185 | else | 1244 | wake_up_locked(&cc->write_thread_wait); |
| 1186 | generic_make_request(clone); | 1245 | spin_unlock_irqrestore(&cc->write_thread_wait.lock, flags); |
| 1187 | } | 1246 | } |
| 1188 | 1247 | ||
| 1189 | static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) | 1248 | static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) |
| @@ -1426,6 +1485,9 @@ static void crypt_dtr(struct dm_target *ti) | |||
| 1426 | if (!cc) | 1485 | if (!cc) |
| 1427 | return; | 1486 | return; |
| 1428 | 1487 | ||
| 1488 | if (cc->write_thread) | ||
| 1489 | kthread_stop(cc->write_thread); | ||
| 1490 | |||
| 1429 | if (cc->io_queue) | 1491 | if (cc->io_queue) |
| 1430 | destroy_workqueue(cc->io_queue); | 1492 | destroy_workqueue(cc->io_queue); |
| 1431 | if (cc->crypt_queue) | 1493 | if (cc->crypt_queue) |
| @@ -1764,6 +1826,18 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 1764 | goto bad; | 1826 | goto bad; |
| 1765 | } | 1827 | } |
| 1766 | 1828 | ||
| 1829 | init_waitqueue_head(&cc->write_thread_wait); | ||
| 1830 | INIT_LIST_HEAD(&cc->write_thread_list); | ||
| 1831 | |||
| 1832 | cc->write_thread = kthread_create(dmcrypt_write, cc, "dmcrypt_write"); | ||
| 1833 | if (IS_ERR(cc->write_thread)) { | ||
| 1834 | ret = PTR_ERR(cc->write_thread); | ||
| 1835 | |||
