summaryrefslogtreecommitdiffstats
path: root/drivers/lightnvm
diff options
context:
space:
mode:
authorMatias Bjørling <m@bjorling.me>2016-07-07 03:54:20 -0400
committerJens Axboe <axboe@fb.com>2016-07-07 10:51:52 -0400
commit855cdd2c0bff7dcf9337734dc5749d127ca05086 (patch)
tree6c08bd87536747d8016248272f607316689b147c /drivers/lightnvm
parent41285fad511a2c3746bee7ccb3b2f21b70305c14 (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.c108
-rw-r--r--drivers/lightnvm/rrpc.h1
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 */
178static void rrpc_set_lun_cur(struct rrpc_lun *rlun, struct rrpc_block *rblk) 178static 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
192static struct rrpc_block *rrpc_get_blk(struct rrpc *rrpc, struct rrpc_lun *rlun, 191static 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 */
588static struct rrpc_addr *rrpc_map_page(struct rrpc *rrpc, sector_t laddr, 585static 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;
605retry: 617retry:
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 */ 624new_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;
656done:
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);
634err:
635 spin_unlock(&rlun->lock);
636 return NULL;
637} 659}
638 660
639static void rrpc_run_gc(struct rrpc *rrpc, struct rrpc_block *rblk) 661static 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