diff options
author | Javier González <jg@lightnvm.io> | 2017-06-30 11:56:40 -0400 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2017-06-30 13:08:18 -0400 |
commit | ee8d5c1ad54e48ec44b6ae9cf91144fcab6ebf83 (patch) | |
tree | cd5e6be51067d2eb47fa42c031a723644593d94f | |
parent | de54e703a4229e4688eb77b32b1c27861384e22a (diff) |
lightnvm: pblk: remove target using async. I/Os
When removing a pblk instance, pad the current line using asynchronous
I/O. This reduces the removal time from ~1 minute in the worst case to a
couple of seconds.
Signed-off-by: Javier González <javier@cnexlabs.com>
Signed-off-by: Matias Bjørling <matias@cnexlabs.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r-- | drivers/lightnvm/pblk-core.c | 5 | ||||
-rw-r--r-- | drivers/lightnvm/pblk-init.c | 9 | ||||
-rw-r--r-- | drivers/lightnvm/pblk-rb.c | 8 | ||||
-rw-r--r-- | drivers/lightnvm/pblk-recovery.c | 163 | ||||
-rw-r--r-- | drivers/lightnvm/pblk-write.c | 2 | ||||
-rw-r--r-- | drivers/lightnvm/pblk.h | 8 |
6 files changed, 122 insertions, 73 deletions
diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c index 74b8d9db05e1..e6f42cddc8ec 100644 --- a/drivers/lightnvm/pblk-core.c +++ b/drivers/lightnvm/pblk-core.c | |||
@@ -273,9 +273,10 @@ static void pblk_flush_writer(struct pblk *pblk) | |||
273 | { | 273 | { |
274 | pblk_rb_flush(&pblk->rwb); | 274 | pblk_rb_flush(&pblk->rwb); |
275 | do { | 275 | do { |
276 | if (!pblk_rb_read_count(&pblk->rwb)) | 276 | if (!pblk_rb_sync_count(&pblk->rwb)) |
277 | break; | 277 | break; |
278 | 278 | ||
279 | pblk_write_kick(pblk); | ||
279 | schedule(); | 280 | schedule(); |
280 | } while (1); | 281 | } while (1); |
281 | } | 282 | } |
@@ -1350,6 +1351,7 @@ void pblk_pipeline_stop(struct pblk *pblk) | |||
1350 | return; | 1351 | return; |
1351 | } | 1352 | } |
1352 | 1353 | ||
1354 | flush_workqueue(pblk->bb_wq); | ||
1353 | pblk_line_close_meta_sync(pblk); | 1355 | pblk_line_close_meta_sync(pblk); |
1354 | 1356 | ||
1355 | spin_lock(&l_mg->free_lock); | 1357 | spin_lock(&l_mg->free_lock); |
@@ -1547,6 +1549,7 @@ void pblk_line_close_meta_sync(struct pblk *pblk) | |||
1547 | } | 1549 | } |
1548 | 1550 | ||
1549 | pblk_wait_for_meta(pblk); | 1551 | pblk_wait_for_meta(pblk); |
1552 | flush_workqueue(pblk->close_wq); | ||
1550 | } | 1553 | } |
1551 | 1554 | ||
1552 | static void pblk_line_should_sync_meta(struct pblk *pblk) | 1555 | static void pblk_line_should_sync_meta(struct pblk *pblk) |
diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c index b3fc310aa51c..025d8fe52154 100644 --- a/drivers/lightnvm/pblk-init.c +++ b/drivers/lightnvm/pblk-init.c | |||
@@ -841,6 +841,15 @@ static int pblk_writer_init(struct pblk *pblk) | |||
841 | 841 | ||
842 | static void pblk_writer_stop(struct pblk *pblk) | 842 | static void pblk_writer_stop(struct pblk *pblk) |
843 | { | 843 | { |
844 | /* The pipeline must be stopped and the write buffer emptied before the | ||
845 | * write thread is stopped | ||
846 | */ | ||
847 | WARN(pblk_rb_read_count(&pblk->rwb), | ||
848 | "Stopping not fully persisted write buffer\n"); | ||
849 | |||
850 | WARN(pblk_rb_sync_count(&pblk->rwb), | ||
851 | "Stopping not fully synced write buffer\n"); | ||
852 | |||
844 | if (pblk->writer_ts) | 853 | if (pblk->writer_ts) |
845 | kthread_stop(pblk->writer_ts); | 854 | kthread_stop(pblk->writer_ts); |
846 | del_timer(&pblk->wtimer); | 855 | del_timer(&pblk->wtimer); |
diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c index 2dda874af890..7300be98e831 100644 --- a/drivers/lightnvm/pblk-rb.c +++ b/drivers/lightnvm/pblk-rb.c | |||
@@ -180,6 +180,14 @@ unsigned int pblk_rb_read_count(struct pblk_rb *rb) | |||
180 | return pblk_rb_ring_count(mem, subm, rb->nr_entries); | 180 | return pblk_rb_ring_count(mem, subm, rb->nr_entries); |
181 | } | 181 | } |
182 | 182 | ||
183 | unsigned int pblk_rb_sync_count(struct pblk_rb *rb) | ||
184 | { | ||
185 | unsigned int mem = READ_ONCE(rb->mem); | ||
186 | unsigned int sync = READ_ONCE(rb->sync); | ||
187 | |||
188 | return pblk_rb_ring_count(mem, sync, rb->nr_entries); | ||
189 | } | ||
190 | |||
183 | unsigned int pblk_rb_read_commit(struct pblk_rb *rb, unsigned int nr_entries) | 191 | unsigned int pblk_rb_read_commit(struct pblk_rb *rb, unsigned int nr_entries) |
184 | { | 192 | { |
185 | unsigned int subm; | 193 | unsigned int subm; |
diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c index 6d58659fa3da..0e48d3e4e143 100644 --- a/drivers/lightnvm/pblk-recovery.c +++ b/drivers/lightnvm/pblk-recovery.c | |||
@@ -327,47 +327,94 @@ next_read_rq: | |||
327 | return 0; | 327 | return 0; |
328 | } | 328 | } |
329 | 329 | ||
330 | static void pblk_recov_complete(struct kref *ref) | ||
331 | { | ||
332 | struct pblk_pad_rq *pad_rq = container_of(ref, struct pblk_pad_rq, ref); | ||
333 | |||
334 | complete(&pad_rq->wait); | ||
335 | } | ||
336 | |||
337 | static void pblk_end_io_recov(struct nvm_rq *rqd) | ||
338 | { | ||
339 | struct pblk_pad_rq *pad_rq = rqd->private; | ||
340 | struct pblk *pblk = pad_rq->pblk; | ||
341 | struct nvm_tgt_dev *dev = pblk->dev; | ||
342 | |||
343 | kref_put(&pad_rq->ref, pblk_recov_complete); | ||
344 | nvm_dev_dma_free(dev->parent, rqd->meta_list, rqd->dma_meta_list); | ||
345 | pblk_free_rqd(pblk, rqd, WRITE); | ||
346 | } | ||
347 | |||
330 | static int pblk_recov_pad_oob(struct pblk *pblk, struct pblk_line *line, | 348 | static int pblk_recov_pad_oob(struct pblk *pblk, struct pblk_line *line, |
331 | struct pblk_recov_alloc p, int left_ppas) | 349 | int left_ppas) |
332 | { | 350 | { |
333 | struct nvm_tgt_dev *dev = pblk->dev; | 351 | struct nvm_tgt_dev *dev = pblk->dev; |
334 | struct nvm_geo *geo = &dev->geo; | 352 | struct nvm_geo *geo = &dev->geo; |
335 | struct ppa_addr *ppa_list; | 353 | struct ppa_addr *ppa_list; |
336 | struct pblk_sec_meta *meta_list; | 354 | struct pblk_sec_meta *meta_list; |
355 | struct pblk_pad_rq *pad_rq; | ||
337 | struct nvm_rq *rqd; | 356 | struct nvm_rq *rqd; |
338 | struct bio *bio; | 357 | struct bio *bio; |
339 | void *data; | 358 | void *data; |
340 | dma_addr_t dma_ppa_list, dma_meta_list; | 359 | dma_addr_t dma_ppa_list, dma_meta_list; |
341 | __le64 *lba_list = emeta_to_lbas(pblk, line->emeta->buf); | 360 | __le64 *lba_list = emeta_to_lbas(pblk, line->emeta->buf); |
342 | u64 w_ptr = line->cur_sec; | 361 | u64 w_ptr = line->cur_sec; |
343 | int left_line_ppas = line->left_msecs; | 362 | int left_line_ppas, rq_ppas, rq_len; |
344 | int rq_ppas, rq_len; | ||
345 | int i, j; | 363 | int i, j; |
346 | int ret = 0; | 364 | int ret = 0; |
347 | DECLARE_COMPLETION_ONSTACK(wait); | ||
348 | 365 | ||
349 | ppa_list = p.ppa_list; | 366 | spin_lock(&line->lock); |
350 | meta_list = p.meta_list; | 367 | left_line_ppas = line->left_msecs; |
351 | rqd = p.rqd; | 368 | spin_unlock(&line->lock); |
352 | data = p.data; | 369 | |
353 | dma_ppa_list = p.dma_ppa_list; | 370 | pad_rq = kmalloc(sizeof(struct pblk_pad_rq), GFP_KERNEL); |
354 | dma_meta_list = p.dma_meta_list; | 371 | if (!pad_rq) |
372 | return -ENOMEM; | ||
373 | |||
374 | data = vzalloc(pblk->max_write_pgs * geo->sec_size); | ||
375 | if (!data) { | ||
376 | ret = -ENOMEM; | ||
377 | goto free_rq; | ||
378 | } | ||
379 | |||
380 | pad_rq->pblk = pblk; | ||
381 | init_completion(&pad_rq->wait); | ||
382 | kref_init(&pad_rq->ref); | ||
355 | 383 | ||
356 | next_pad_rq: | 384 | next_pad_rq: |
357 | rq_ppas = pblk_calc_secs(pblk, left_ppas, 0); | 385 | rq_ppas = pblk_calc_secs(pblk, left_ppas, 0); |
358 | if (!rq_ppas) | 386 | if (rq_ppas < pblk->min_write_pgs) { |
359 | rq_ppas = pblk->min_write_pgs; | 387 | pr_err("pblk: corrupted pad line %d\n", line->id); |
388 | goto free_rq; | ||
389 | } | ||
390 | |||
360 | rq_len = rq_ppas * geo->sec_size; | 391 | rq_len = rq_ppas * geo->sec_size; |
361 | 392 | ||
393 | meta_list = nvm_dev_dma_alloc(dev->parent, GFP_KERNEL, &dma_meta_list); | ||
394 | if (!meta_list) { | ||
395 | ret = -ENOMEM; | ||
396 | goto free_data; | ||
397 | } | ||
398 | |||
399 | ppa_list = (void *)(meta_list) + pblk_dma_meta_size; | ||
400 | dma_ppa_list = dma_meta_list + pblk_dma_meta_size; | ||
401 | |||
402 | rqd = pblk_alloc_rqd(pblk, WRITE); | ||
403 | if (IS_ERR(rqd)) { | ||
404 | ret = PTR_ERR(rqd); | ||
405 | goto fail_free_meta; | ||
406 | } | ||
407 | memset(rqd, 0, pblk_w_rq_size); | ||
408 | |||
362 | bio = bio_map_kern(dev->q, data, rq_len, GFP_KERNEL); | 409 | bio = bio_map_kern(dev->q, data, rq_len, GFP_KERNEL); |
363 | if (IS_ERR(bio)) | 410 | if (IS_ERR(bio)) { |
364 | return PTR_ERR(bio); | 411 | ret = PTR_ERR(bio); |
412 | goto fail_free_rqd; | ||
413 | } | ||
365 | 414 | ||
366 | bio->bi_iter.bi_sector = 0; /* internal bio */ | 415 | bio->bi_iter.bi_sector = 0; /* internal bio */ |
367 | bio_set_op_attrs(bio, REQ_OP_WRITE, 0); | 416 | bio_set_op_attrs(bio, REQ_OP_WRITE, 0); |
368 | 417 | ||
369 | memset(rqd, 0, pblk_g_rq_size); | ||
370 | |||
371 | rqd->bio = bio; | 418 | rqd->bio = bio; |
372 | rqd->opcode = NVM_OP_PWRITE; | 419 | rqd->opcode = NVM_OP_PWRITE; |
373 | rqd->flags = pblk_set_progr_mode(pblk, WRITE); | 420 | rqd->flags = pblk_set_progr_mode(pblk, WRITE); |
@@ -376,8 +423,8 @@ next_pad_rq: | |||
376 | rqd->ppa_list = ppa_list; | 423 | rqd->ppa_list = ppa_list; |
377 | rqd->dma_ppa_list = dma_ppa_list; | 424 | rqd->dma_ppa_list = dma_ppa_list; |
378 | rqd->dma_meta_list = dma_meta_list; | 425 | rqd->dma_meta_list = dma_meta_list; |
379 | rqd->end_io = pblk_end_io_sync; | 426 | rqd->end_io = pblk_end_io_recov; |
380 | rqd->private = &wait; | 427 | rqd->private = pad_rq; |
381 | 428 | ||
382 | for (i = 0; i < rqd->nr_ppas; ) { | 429 | for (i = 0; i < rqd->nr_ppas; ) { |
383 | struct ppa_addr ppa; | 430 | struct ppa_addr ppa; |
@@ -405,25 +452,41 @@ next_pad_rq: | |||
405 | } | 452 | } |
406 | } | 453 | } |
407 | 454 | ||
455 | kref_get(&pad_rq->ref); | ||
456 | |||
408 | ret = pblk_submit_io(pblk, rqd); | 457 | ret = pblk_submit_io(pblk, rqd); |
409 | if (ret) { | 458 | if (ret) { |
410 | pr_err("pblk: I/O submission failed: %d\n", ret); | 459 | pr_err("pblk: I/O submission failed: %d\n", ret); |
411 | return ret; | 460 | goto free_data; |
412 | } | 461 | } |
413 | 462 | ||
414 | if (!wait_for_completion_io_timeout(&wait, | ||
415 | msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { | ||
416 | pr_err("pblk: L2P recovery write timed out\n"); | ||
417 | } | ||
418 | atomic_dec(&pblk->inflight_io); | 463 | atomic_dec(&pblk->inflight_io); |
419 | reinit_completion(&wait); | ||
420 | 464 | ||
421 | left_line_ppas -= rq_ppas; | 465 | left_line_ppas -= rq_ppas; |
422 | left_ppas -= rq_ppas; | 466 | left_ppas -= rq_ppas; |
423 | if (left_ppas > 0 && left_line_ppas) | 467 | if (left_ppas && left_line_ppas) |
424 | goto next_pad_rq; | 468 | goto next_pad_rq; |
425 | 469 | ||
426 | return 0; | 470 | kref_put(&pad_rq->ref, pblk_recov_complete); |
471 | |||
472 | if (!wait_for_completion_io_timeout(&pad_rq->wait, | ||
473 | msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { | ||
474 | pr_err("pblk: pad write timed out\n"); | ||
475 | ret = -ETIME; | ||
476 | } | ||
477 | |||
478 | free_rq: | ||
479 | kfree(pad_rq); | ||
480 | free_data: | ||
481 | vfree(data); | ||
482 | return ret; | ||
483 | |||
484 | fail_free_rqd: | ||
485 | pblk_free_rqd(pblk, rqd, WRITE); | ||
486 | fail_free_meta: | ||
487 | nvm_dev_dma_free(dev->parent, meta_list, dma_meta_list); | ||
488 | kfree(pad_rq); | ||
489 | return ret; | ||
427 | } | 490 | } |
428 | 491 | ||
429 | /* When this function is called, it means that not all upper pages have been | 492 | /* When this function is called, it means that not all upper pages have been |
@@ -555,7 +618,7 @@ next_rq: | |||
555 | if (pad_secs > line->left_msecs) | 618 | if (pad_secs > line->left_msecs) |
556 | pad_secs = line->left_msecs; | 619 | pad_secs = line->left_msecs; |
557 | 620 | ||
558 | ret = pblk_recov_pad_oob(pblk, line, p, pad_secs); | 621 | ret = pblk_recov_pad_oob(pblk, line, pad_secs); |
559 | if (ret) | 622 | if (ret) |
560 | pr_err("pblk: OOB padding failed (err:%d)\n", ret); | 623 | pr_err("pblk: OOB padding failed (err:%d)\n", ret); |
561 | 624 | ||
@@ -961,64 +1024,22 @@ out: | |||
961 | */ | 1024 | */ |
962 | int pblk_recov_pad(struct pblk *pblk) | 1025 | int pblk_recov_pad(struct pblk *pblk) |
963 | { | 1026 | { |
964 | struct nvm_tgt_dev *dev = pblk->dev; | ||
965 | struct nvm_geo *geo = &dev->geo; | ||
966 | struct pblk_line *line; | 1027 | struct pblk_line *line; |
967 | struct pblk_line_mgmt *l_mg = &pblk->l_mg; | 1028 | struct pblk_line_mgmt *l_mg = &pblk->l_mg; |
968 | struct nvm_rq *rqd; | ||
969 | struct pblk_recov_alloc p; | ||
970 | struct ppa_addr *ppa_list; | ||
971 | struct pblk_sec_meta *meta_list; | ||
972 | void *data; | ||
973 | int left_msecs; | 1029 | int left_msecs; |
974 | int ret = 0; | 1030 | int ret = 0; |
975 | dma_addr_t dma_ppa_list, dma_meta_list; | ||
976 | 1031 | ||
977 | spin_lock(&l_mg->free_lock); | 1032 | spin_lock(&l_mg->free_lock); |
978 | line = l_mg->data_line; | 1033 | line = l_mg->data_line; |
979 | left_msecs = line->left_msecs; | 1034 | left_msecs = line->left_msecs; |
980 | spin_unlock(&l_mg->free_lock); | 1035 | spin_unlock(&l_mg->free_lock); |
981 | 1036 | ||
982 | rqd = pblk_alloc_rqd(pblk, READ); | 1037 | ret = pblk_recov_pad_oob(pblk, line, left_msecs); |
983 | if (IS_ERR(rqd)) | ||
984 | return PTR_ERR(rqd); | ||
985 | |||
986 | meta_list = nvm_dev_dma_alloc(dev->parent, GFP_KERNEL, &dma_meta_list); | ||
987 | if (!meta_list) { | ||
988 | ret = -ENOMEM; | ||
989 | goto free_rqd; | ||
990 | } | ||
991 | |||
992 | ppa_list = (void *)(meta_list) + pblk_dma_meta_size; | ||
993 | dma_ppa_list = dma_meta_list + pblk_dma_meta_size; | ||
994 | |||
995 | data = kcalloc(pblk->max_write_pgs, geo->sec_size, GFP_KERNEL); | ||
996 | if (!data) { | ||
997 | ret = -ENOMEM; | ||
998 | goto free_meta_list; | ||
999 | } | ||
1000 | |||
1001 | p.ppa_list = ppa_list; | ||
1002 | p.meta_list = meta_list; | ||
1003 | p.rqd = rqd; | ||
1004 | p.data = data; | ||
1005 | p.dma_ppa_list = dma_ppa_list; | ||
1006 | p.dma_meta_list = dma_meta_list; | ||
1007 | |||
1008 | ret = pblk_recov_pad_oob(pblk, line, p, left_msecs); | ||
1009 | if (ret) { | 1038 | if (ret) { |
1010 | pr_err("pblk: Tear down padding failed (%d)\n", ret); | 1039 | pr_err("pblk: Tear down padding failed (%d)\n", ret); |
1011 | goto free_data; | 1040 | return ret; |
1012 | } | 1041 | } |
1013 | 1042 | ||
1014 | pblk_line_close_meta(pblk, line); | 1043 | pblk_line_close_meta(pblk, line); |
1015 | |||
1016 | free_data: | ||
1017 | kfree(data); | ||
1018 | free_meta_list: | ||
1019 | nvm_dev_dma_free(dev->parent, meta_list, dma_meta_list); | ||
1020 | free_rqd: | ||
1021 | pblk_free_rqd(pblk, rqd, READ); | ||
1022 | |||
1023 | return ret; | 1044 | return ret; |
1024 | } | 1045 | } |
diff --git a/drivers/lightnvm/pblk-write.c b/drivers/lightnvm/pblk-write.c index 8151bf4bb945..d62a8f4faaf4 100644 --- a/drivers/lightnvm/pblk-write.c +++ b/drivers/lightnvm/pblk-write.c | |||
@@ -190,7 +190,7 @@ static void pblk_end_io_write_meta(struct nvm_rq *rqd) | |||
190 | 190 | ||
191 | if (rqd->error) { | 191 | if (rqd->error) { |
192 | pblk_log_write_err(pblk, rqd); | 192 | pblk_log_write_err(pblk, rqd); |
193 | pr_err("pblk: metadata I/O failed\n"); | 193 | pr_err("pblk: metadata I/O failed. Line %d\n", line->id); |
194 | } | 194 | } |
195 | #ifdef CONFIG_NVM_DEBUG | 195 | #ifdef CONFIG_NVM_DEBUG |
196 | else | 196 | else |
diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index cdad2c9edbdf..bf5b73fb345f 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h | |||
@@ -111,6 +111,13 @@ struct pblk_g_ctx { | |||
111 | void *private; | 111 | void *private; |
112 | }; | 112 | }; |
113 | 113 | ||
114 | /* Pad context */ | ||
115 | struct pblk_pad_rq { | ||
116 | struct pblk *pblk; | ||
117 | struct completion wait; | ||
118 | struct kref ref; | ||
119 | }; | ||
120 | |||
114 | /* Recovery context */ | 121 | /* Recovery context */ |
115 | struct pblk_rec_ctx { | 122 | struct pblk_rec_ctx { |
116 | struct pblk *pblk; | 123 | struct pblk *pblk; |
@@ -674,6 +681,7 @@ void pblk_rb_sync_end(struct pblk_rb *rb, unsigned long *flags); | |||
674 | unsigned int pblk_rb_sync_point_count(struct pblk_rb *rb); | 681 | unsigned int pblk_rb_sync_point_count(struct pblk_rb *rb); |
675 | 682 | ||
676 | unsigned int pblk_rb_read_count(struct pblk_rb *rb); | 683 | unsigned int pblk_rb_read_count(struct pblk_rb *rb); |
684 | unsigned int pblk_rb_sync_count(struct pblk_rb *rb); | ||
677 | unsigned int pblk_rb_wrap_pos(struct pblk_rb *rb, unsigned int pos); | 685 | unsigned int pblk_rb_wrap_pos(struct pblk_rb *rb, unsigned int pos); |
678 | 686 | ||
679 | int pblk_rb_tear_down_check(struct pblk_rb *rb); | 687 | int pblk_rb_tear_down_check(struct pblk_rb *rb); |