diff options
author | Matias Bjørling <m@bjorling.me> | 2016-07-07 03:54:20 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2016-07-07 10:51:52 -0400 |
commit | 855cdd2c0bff7dcf9337734dc5749d127ca05086 (patch) | |
tree | 6c08bd87536747d8016248272f607316689b147c /drivers/lightnvm | |
parent | 41285fad511a2c3746bee7ccb3b2f21b70305c14 (diff) |
lightnvm: make rrpc_map_page call nvm_get_blk outside locks
The nvm_get_blk() function is called with rlun->lock held. This is ok
when the media manager implementation doesn't go out of its atomic
context. However, if a media manager persists its metadata, and
guarantees that the block is given to the target, this is no longer
a viable approach. Therefore, clean up the flow of rrpc_map_page,
and make sure that nvm_get_blk() is called without any locks acquired.
Signed-off-by: Matias Bjørling <m@bjorling.me>
Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'drivers/lightnvm')
-rw-r--r-- | drivers/lightnvm/rrpc.c | 108 | ||||
-rw-r--r-- | drivers/lightnvm/rrpc.h | 1 |
2 files changed, 66 insertions, 43 deletions
diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c index fa8d5be2987c..fa1ab0421489 100644 --- a/drivers/lightnvm/rrpc.c +++ b/drivers/lightnvm/rrpc.c | |||
@@ -175,18 +175,17 @@ static struct ppa_addr rrpc_ppa_to_gaddr(struct nvm_dev *dev, u64 addr) | |||
175 | } | 175 | } |
176 | 176 | ||
177 | /* requires lun->lock taken */ | 177 | /* requires lun->lock taken */ |
178 | static void rrpc_set_lun_cur(struct rrpc_lun *rlun, struct rrpc_block *rblk) | 178 | static void rrpc_set_lun_cur(struct rrpc_lun *rlun, struct rrpc_block *new_rblk, |
179 | struct rrpc_block **cur_rblk) | ||
179 | { | 180 | { |
180 | struct rrpc *rrpc = rlun->rrpc; | 181 | struct rrpc *rrpc = rlun->rrpc; |
181 | 182 | ||
182 | BUG_ON(!rblk); | 183 | if (*cur_rblk) { |
183 | 184 | spin_lock(&(*cur_rblk)->lock); | |
184 | if (rlun->cur) { | 185 | WARN_ON(!block_is_full(rrpc, *cur_rblk)); |
185 | spin_lock(&rlun->cur->lock); | 186 | spin_unlock(&(*cur_rblk)->lock); |
186 | WARN_ON(!block_is_full(rrpc, rlun->cur)); | ||
187 | spin_unlock(&rlun->cur->lock); | ||
188 | } | 187 | } |
189 | rlun->cur = rblk; | 188 | *cur_rblk = new_rblk; |
190 | } | 189 | } |
191 | 190 | ||
192 | static struct rrpc_block *rrpc_get_blk(struct rrpc *rrpc, struct rrpc_lun *rlun, | 191 | static struct rrpc_block *rrpc_get_blk(struct rrpc *rrpc, struct rrpc_lun *rlun, |
@@ -577,21 +576,20 @@ out: | |||
577 | return addr; | 576 | return addr; |
578 | } | 577 | } |
579 | 578 | ||
580 | /* Simple round-robin Logical to physical address translation. | 579 | /* Map logical address to a physical page. The mapping implements a round robin |
581 | * | 580 | * approach and allocates a page from the next lun available. |
582 | * Retrieve the mapping using the active append point. Then update the ap for | ||
583 | * the next write to the disk. | ||
584 | * | 581 | * |
585 | * Returns rrpc_addr with the physical address and block. Remember to return to | 582 | * Returns rrpc_addr with the physical address and block. Returns NULL if no |
586 | * rrpc->addr_cache when request is finished. | 583 | * blocks in the next rlun are available. |
587 | */ | 584 | */ |
588 | static struct rrpc_addr *rrpc_map_page(struct rrpc *rrpc, sector_t laddr, | 585 | static struct rrpc_addr *rrpc_map_page(struct rrpc *rrpc, sector_t laddr, |
589 | int is_gc) | 586 | int is_gc) |
590 | { | 587 | { |
591 | struct rrpc_lun *rlun; | 588 | struct rrpc_lun *rlun; |
592 | struct rrpc_block *rblk; | 589 | struct rrpc_block *rblk, **cur_rblk; |
593 | struct nvm_lun *lun; | 590 | struct nvm_lun *lun; |
594 | u64 paddr; | 591 | u64 paddr; |
592 | int gc_force = 0; | ||
595 | 593 | ||
596 | rlun = rrpc_get_lun_rr(rrpc, is_gc); | 594 | rlun = rrpc_get_lun_rr(rrpc, is_gc); |
597 | lun = rlun->parent; | 595 | lun = rlun->parent; |
@@ -599,41 +597,65 @@ static struct rrpc_addr *rrpc_map_page(struct rrpc *rrpc, sector_t laddr, | |||
599 | if (!is_gc && lun->nr_free_blocks < rrpc->nr_luns * 4) | 597 | if (!is_gc && lun->nr_free_blocks < rrpc->nr_luns * 4) |
600 | return NULL; | 598 | return NULL; |
601 | 599 | ||
602 | spin_lock(&rlun->lock); | 600 | /* |
601 | * page allocation steps: | ||
602 | * 1. Try to allocate new page from current rblk | ||
603 | * 2a. If succeed, proceed to map it in and return | ||
604 | * 2b. If fail, first try to allocate a new block from media manger, | ||
605 | * and then retry step 1. Retry until the normal block pool is | ||
606 | * exhausted. | ||
607 | * 3. If exhausted, and garbage collector is requesting the block, | ||
608 | * go to the reserved block and retry step 1. | ||
609 | * In the case that this fails as well, or it is not GC | ||
610 | * requesting, report not able to retrieve a block and let the | ||
611 | * caller handle further processing. | ||
612 | */ | ||
603 | 613 | ||
614 | spin_lock(&rlun->lock); | ||
615 | cur_rblk = &rlun->cur; | ||
604 | rblk = rlun->cur; | 616 | rblk = rlun->cur; |
605 | retry: | 617 | retry: |
606 | paddr = rrpc_alloc_addr(rrpc, rblk); | 618 | paddr = rrpc_alloc_addr(rrpc, rblk); |
607 | 619 | ||
608 | if (paddr == ADDR_EMPTY) { | 620 | if (paddr != ADDR_EMPTY) |
609 | rblk = rrpc_get_blk(rrpc, rlun, 0); | 621 | goto done; |
610 | if (rblk) { | ||
611 | rrpc_set_lun_cur(rlun, rblk); | ||
612 | goto retry; | ||
613 | } | ||
614 | 622 | ||
615 | if (is_gc) { | 623 | if (!list_empty(&rlun->wblk_list)) { |
616 | /* retry from emergency gc block */ | 624 | new_blk: |
617 | paddr = rrpc_alloc_addr(rrpc, rlun->gc_cur); | 625 | rblk = list_first_entry(&rlun->wblk_list, struct rrpc_block, |
618 | if (paddr == ADDR_EMPTY) { | 626 | prio); |
619 | rblk = rrpc_get_blk(rrpc, rlun, 1); | 627 | rrpc_set_lun_cur(rlun, rblk, cur_rblk); |
620 | if (!rblk) { | 628 | list_del(&rblk->prio); |
621 | pr_err("rrpc: no more blocks"); | 629 | goto retry; |
622 | goto err; | 630 | } |
623 | } | 631 | spin_unlock(&rlun->lock); |
624 | 632 | ||
625 | rlun->gc_cur = rblk; | 633 | rblk = rrpc_get_blk(rrpc, rlun, gc_force); |
626 | paddr = rrpc_alloc_addr(rrpc, rlun->gc_cur); | 634 | if (rblk) { |
627 | } | 635 | spin_lock(&rlun->lock); |
628 | rblk = rlun->gc_cur; | 636 | list_add_tail(&rblk->prio, &rlun->wblk_list); |
629 | } | 637 | /* |
638 | * another thread might already have added a new block, | ||
639 | * Therefore, make sure that one is used, instead of the | ||
640 | * one just added. | ||
641 | */ | ||
642 | goto new_blk; | ||
643 | } | ||
644 | |||
645 | if (unlikely(is_gc) && !gc_force) { | ||
646 | /* retry from emergency gc block */ | ||
647 | cur_rblk = &rlun->gc_cur; | ||
648 | rblk = rlun->gc_cur; | ||
649 | gc_force = 1; | ||
650 | spin_lock(&rlun->lock); | ||
651 | goto retry; | ||
630 | } | 652 | } |
631 | 653 | ||
654 | pr_err("rrpc: failed to allocate new block\n"); | ||
655 | return NULL; | ||
656 | done: | ||
632 | spin_unlock(&rlun->lock); | 657 | spin_unlock(&rlun->lock); |
633 | return rrpc_update_map(rrpc, laddr, rblk, paddr); | 658 | return rrpc_update_map(rrpc, laddr, rblk, paddr); |
634 | err: | ||
635 | spin_unlock(&rlun->lock); | ||
636 | return NULL; | ||
637 | } | 659 | } |
638 | 660 | ||
639 | static void rrpc_run_gc(struct rrpc *rrpc, struct rrpc_block *rblk) | 661 | static void rrpc_run_gc(struct rrpc *rrpc, struct rrpc_block *rblk) |
@@ -1177,6 +1199,7 @@ static int rrpc_luns_init(struct rrpc *rrpc, int lun_begin, int lun_end) | |||
1177 | 1199 | ||
1178 | rlun->rrpc = rrpc; | 1200 | rlun->rrpc = rrpc; |
1179 | INIT_LIST_HEAD(&rlun->prio_list); | 1201 | INIT_LIST_HEAD(&rlun->prio_list); |
1202 | INIT_LIST_HEAD(&rlun->wblk_list); | ||
1180 | 1203 | ||
1181 | INIT_WORK(&rlun->ws_gc, rrpc_lun_gc); | 1204 | INIT_WORK(&rlun->ws_gc, rrpc_lun_gc); |
1182 | spin_lock_init(&rlun->lock); | 1205 | spin_lock_init(&rlun->lock); |
@@ -1317,14 +1340,13 @@ static int rrpc_luns_configure(struct rrpc *rrpc) | |||
1317 | rblk = rrpc_get_blk(rrpc, rlun, 0); | 1340 | rblk = rrpc_get_blk(rrpc, rlun, 0); |
1318 | if (!rblk) | 1341 | if (!rblk) |
1319 | goto err; | 1342 | goto err; |
1320 | 1343 | rrpc_set_lun_cur(rlun, rblk, &rlun->cur); | |
1321 | rrpc_set_lun_cur(rlun, rblk); | ||
1322 | 1344 | ||
1323 | /* Emergency gc block */ | 1345 | /* Emergency gc block */ |
1324 | rblk = rrpc_get_blk(rrpc, rlun, 1); | 1346 | rblk = rrpc_get_blk(rrpc, rlun, 1); |
1325 | if (!rblk) | 1347 | if (!rblk) |
1326 | goto err; | 1348 | goto err; |
1327 | rlun->gc_cur = rblk; | 1349 | rrpc_set_lun_cur(rlun, rblk, &rlun->gc_cur); |
1328 | } | 1350 | } |
1329 | 1351 | ||
1330 | return 0; | 1352 | return 0; |
diff --git a/drivers/lightnvm/rrpc.h b/drivers/lightnvm/rrpc.h index 448e39a9c515..5e87d52cb983 100644 --- a/drivers/lightnvm/rrpc.h +++ b/drivers/lightnvm/rrpc.h | |||
@@ -76,6 +76,7 @@ struct rrpc_lun { | |||
76 | struct rrpc_block *blocks; /* Reference to block allocation */ | 76 | struct rrpc_block *blocks; /* Reference to block allocation */ |
77 | 77 | ||
78 | struct list_head prio_list; /* Blocks that may be GC'ed */ | 78 | struct list_head prio_list; /* Blocks that may be GC'ed */ |
79 | struct list_head wblk_list; /* Queued blocks to be written to */ | ||
79 | 80 | ||
80 | struct work_struct ws_gc; | 81 | struct work_struct ws_gc; |
81 | 82 | ||