Skip to content

Commit

Permalink
lightnvm: pblk: fix race condition on GC
Browse files Browse the repository at this point in the history
This patch fixes a race condition where a write is mapped to the last
sectors of a line. The write is synced to the device but the L2P is not
updated yet. When the line is garbage collected before the L2P update
is performed, the sectors are ignored by the GC logic and the line is
freed before all sectors are moved. When the L2P is finally updated, it
contains a mapping to a freed line, subsequent reads of the
corresponding LBAs fail.

This patch introduces a per line counter specifying the number of
sectors that are synced to the device but have not been updated in the
L2P. Lines with a counter of greater than zero will not be selected
for GC.

Signed-off-by: Heiner Litz <[email protected]>
Reviewed-by: Hans Holmberg <[email protected]>
Reviewed-by: Javier González <[email protected]>
Signed-off-by: Matias Bjørling <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
hlitz authored and axboe committed Feb 11, 2019
1 parent b4cdc42 commit 0586942
Show file tree
Hide file tree
Showing 6 changed files with 18 additions and 7 deletions.
1 change: 1 addition & 0 deletions drivers/lightnvm/pblk-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,7 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
spin_unlock(&line->lock);

kref_init(&line->ref);
atomic_set(&line->sec_to_update, 0);

return 0;
}
Expand Down
20 changes: 13 additions & 7 deletions drivers/lightnvm/pblk-gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,16 +365,22 @@ static struct pblk_line *pblk_gc_get_victim_line(struct pblk *pblk,
struct list_head *group_list)
{
struct pblk_line *line, *victim;
int line_vsc, victim_vsc;
unsigned int line_vsc = ~0x0L, victim_vsc = ~0x0L;

victim = list_first_entry(group_list, struct pblk_line, list);

list_for_each_entry(line, group_list, list) {
line_vsc = le32_to_cpu(*line->vsc);
victim_vsc = le32_to_cpu(*victim->vsc);
if (line_vsc < victim_vsc)
if (!atomic_read(&line->sec_to_update))
line_vsc = le32_to_cpu(*line->vsc);
if (line_vsc < victim_vsc) {
victim = line;
victim_vsc = le32_to_cpu(*victim->vsc);
}
}

if (victim_vsc == ~0x0)
return NULL;

return victim;
}

Expand Down Expand Up @@ -448,13 +454,13 @@ static void pblk_gc_run(struct pblk *pblk)

do {
spin_lock(&l_mg->gc_lock);
if (list_empty(group_list)) {

line = pblk_gc_get_victim_line(pblk, group_list);
if (!line) {
spin_unlock(&l_mg->gc_lock);
break;
}

line = pblk_gc_get_victim_line(pblk, group_list);

spin_lock(&line->lock);
WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
line->state = PBLK_LINESTATE_GC;
Expand Down
1 change: 1 addition & 0 deletions drivers/lightnvm/pblk-map.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ static int pblk_map_page_data(struct pblk *pblk, unsigned int sentry,
*/
if (i < valid_secs) {
kref_get(&line->ref);
atomic_inc(&line->sec_to_update);
w_ctx = pblk_rb_w_ctx(&pblk->rwb, sentry + i);
w_ctx->ppa = ppa_list[i];
meta->lba = cpu_to_le64(w_ctx->lba);
Expand Down
1 change: 1 addition & 0 deletions drivers/lightnvm/pblk-rb.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ static int __pblk_rb_update_l2p(struct pblk_rb *rb, unsigned int to_update)
entry->cacheline);

line = pblk_ppa_to_line(pblk, w_ctx->ppa);
atomic_dec(&line->sec_to_update);
kref_put(&line->ref, pblk_line_put);
clean_wctx(w_ctx);
rb->l2p_update = pblk_rb_ptr_wrap(rb, rb->l2p_update, 1);
Expand Down
1 change: 1 addition & 0 deletions drivers/lightnvm/pblk-write.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ static void pblk_prepare_resubmit(struct pblk *pblk, unsigned int sentry,
* re-map these entries
*/
line = pblk_ppa_to_line(pblk, w_ctx->ppa);
atomic_dec(&line->sec_to_update);
kref_put(&line->ref, pblk_line_put);
}
spin_unlock(&pblk->trans_lock);
Expand Down
1 change: 1 addition & 0 deletions drivers/lightnvm/pblk.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ struct pblk_line {
__le32 *vsc; /* Valid sector count in line */

struct kref ref; /* Write buffer L2P references */
atomic_t sec_to_update; /* Outstanding L2P updates to ppa */

struct pblk_w_err_gc *w_err_gc; /* Write error gc recovery metadata */

Expand Down

0 comments on commit 0586942

Please sign in to comment.