diff options
Diffstat (limited to 'drivers/lightnvm/pblk-gc.c')
| -rw-r--r-- | drivers/lightnvm/pblk-gc.c | 52 | 
1 files changed, 31 insertions, 21 deletions
diff --git a/drivers/lightnvm/pblk-gc.c b/drivers/lightnvm/pblk-gc.c index 26a52ea7ec45..63ee205b41c4 100644 --- a/drivers/lightnvm/pblk-gc.c +++ b/drivers/lightnvm/pblk-gc.c @@ -59,24 +59,28 @@ static void pblk_gc_writer_kick(struct pblk_gc *gc)  	wake_up_process(gc->gc_writer_ts);  } -static void pblk_put_line_back(struct pblk *pblk, struct pblk_line *line) +void pblk_put_line_back(struct pblk *pblk, struct pblk_line *line)  {  	struct pblk_line_mgmt *l_mg = &pblk->l_mg;  	struct list_head *move_list; +	spin_lock(&l_mg->gc_lock);  	spin_lock(&line->lock);  	WARN_ON(line->state != PBLK_LINESTATE_GC);  	line->state = PBLK_LINESTATE_CLOSED;  	trace_pblk_line_state(pblk_disk_name(pblk), line->id,  					line->state); + +	/* We need to reset gc_group in order to ensure that +	 * pblk_line_gc_list will return proper move_list +	 * since right now current line is not on any of the +	 * gc lists. +	 */ +	line->gc_group = PBLK_LINEGC_NONE;  	move_list = pblk_line_gc_list(pblk, line);  	spin_unlock(&line->lock); - -	if (move_list) { -		spin_lock(&l_mg->gc_lock); -		list_add_tail(&line->list, move_list); -		spin_unlock(&l_mg->gc_lock); -	} +	list_add_tail(&line->list, move_list); +	spin_unlock(&l_mg->gc_lock);  }  static void pblk_gc_line_ws(struct work_struct *work) @@ -84,8 +88,6 @@ static void pblk_gc_line_ws(struct work_struct *work)  	struct pblk_line_ws *gc_rq_ws = container_of(work,  						struct pblk_line_ws, ws);  	struct pblk *pblk = gc_rq_ws->pblk; -	struct nvm_tgt_dev *dev = pblk->dev; -	struct nvm_geo *geo = &dev->geo;  	struct pblk_gc *gc = &pblk->gc;  	struct pblk_line *line = gc_rq_ws->line;  	struct pblk_gc_rq *gc_rq = gc_rq_ws->priv; @@ -93,18 +95,10 @@ static void pblk_gc_line_ws(struct work_struct *work)  	up(&gc->gc_sem); -	gc_rq->data = vmalloc(array_size(gc_rq->nr_secs, geo->csecs)); -	if (!gc_rq->data) { -		pblk_err(pblk, "could not GC line:%d (%d/%d)\n", -					line->id, *line->vsc, gc_rq->nr_secs); -		goto out; -	} -  	/* Read from GC victim block */  	ret = pblk_submit_read_gc(pblk, gc_rq);  	if (ret) { -		pblk_err(pblk, "failed GC read in line:%d (err:%d)\n", -								line->id, ret); +		line->w_err_gc->has_gc_err = 1;  		goto out;  	} @@ -189,6 +183,8 @@ static void pblk_gc_line_prepare_ws(struct work_struct *work)  	struct pblk_line *line = line_ws->line;  	struct pblk_line_mgmt *l_mg = &pblk->l_mg;  	struct pblk_line_meta *lm = &pblk->lm; +	struct nvm_tgt_dev *dev = pblk->dev; +	struct nvm_geo *geo = &dev->geo;  	struct pblk_gc *gc = &pblk->gc;  	struct pblk_line_ws *gc_rq_ws;  	struct pblk_gc_rq *gc_rq; @@ -247,9 +243,13 @@ next_rq:  	gc_rq->nr_secs = nr_secs;  	gc_rq->line = line; +	gc_rq->data = vmalloc(array_size(gc_rq->nr_secs, geo->csecs)); +	if (!gc_rq->data) +		goto fail_free_gc_rq; +  	gc_rq_ws = kmalloc(sizeof(struct pblk_line_ws), GFP_KERNEL);  	if (!gc_rq_ws) -		goto fail_free_gc_rq; +		goto fail_free_gc_data;  	gc_rq_ws->pblk = pblk;  	gc_rq_ws->line = line; @@ -281,6 +281,8 @@ out:  	return; +fail_free_gc_data: +	vfree(gc_rq->data);  fail_free_gc_rq:  	kfree(gc_rq);  fail_free_lba_list: @@ -290,8 +292,11 @@ fail_free_invalid_bitmap:  fail_free_ws:  	kfree(line_ws); +	/* Line goes back to closed state, so we cannot release additional +	 * reference for line, since we do that only when we want to do +	 * gc to free line state transition. +	 */  	pblk_put_line_back(pblk, line); -	kref_put(&line->ref, pblk_line_put);  	atomic_dec(&gc->read_inflight_gc);  	pblk_err(pblk, "failed to GC line %d\n", line->id); @@ -355,8 +360,13 @@ static int pblk_gc_read(struct pblk *pblk)  	pblk_gc_kick(pblk); -	if (pblk_gc_line(pblk, line)) +	if (pblk_gc_line(pblk, line)) {  		pblk_err(pblk, "failed to GC line %d\n", line->id); +		/* rollback */ +		spin_lock(&gc->r_lock); +		list_add_tail(&line->list, &gc->r_list); +		spin_unlock(&gc->r_lock); +	}  	return 0;  }  |