18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 CNEX Labs
48c2ecf20Sopenharmony_ci * Initial release: Javier Gonzalez <javier@cnexlabs.com>
58c2ecf20Sopenharmony_ci *                  Matias Bjorling <matias@cnexlabs.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
88c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version
98c2ecf20Sopenharmony_ci * 2 as published by the Free Software Foundation.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
128c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
138c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
148c2ecf20Sopenharmony_ci * General Public License for more details.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * pblk-gc.c - pblk's garbage collector
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "pblk.h"
208c2ecf20Sopenharmony_ci#include "pblk-trace.h"
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic void pblk_gc_free_gc_rq(struct pblk_gc_rq *gc_rq)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	if (gc_rq->data)
278c2ecf20Sopenharmony_ci		vfree(gc_rq->data);
288c2ecf20Sopenharmony_ci	kfree(gc_rq);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int pblk_gc_write(struct pblk *pblk)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
348c2ecf20Sopenharmony_ci	struct pblk_gc_rq *gc_rq, *tgc_rq;
358c2ecf20Sopenharmony_ci	LIST_HEAD(w_list);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	spin_lock(&gc->w_lock);
388c2ecf20Sopenharmony_ci	if (list_empty(&gc->w_list)) {
398c2ecf20Sopenharmony_ci		spin_unlock(&gc->w_lock);
408c2ecf20Sopenharmony_ci		return 1;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	list_cut_position(&w_list, &gc->w_list, gc->w_list.prev);
448c2ecf20Sopenharmony_ci	gc->w_entries = 0;
458c2ecf20Sopenharmony_ci	spin_unlock(&gc->w_lock);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	list_for_each_entry_safe(gc_rq, tgc_rq, &w_list, list) {
488c2ecf20Sopenharmony_ci		pblk_write_gc_to_cache(pblk, gc_rq);
498c2ecf20Sopenharmony_ci		list_del(&gc_rq->list);
508c2ecf20Sopenharmony_ci		kref_put(&gc_rq->line->ref, pblk_line_put);
518c2ecf20Sopenharmony_ci		pblk_gc_free_gc_rq(gc_rq);
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void pblk_gc_writer_kick(struct pblk_gc *gc)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	wake_up_process(gc->gc_writer_ts);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_civoid pblk_put_line_back(struct pblk *pblk, struct pblk_line *line)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct pblk_line_mgmt *l_mg = &pblk->l_mg;
658c2ecf20Sopenharmony_ci	struct list_head *move_list;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	spin_lock(&l_mg->gc_lock);
688c2ecf20Sopenharmony_ci	spin_lock(&line->lock);
698c2ecf20Sopenharmony_ci	WARN_ON(line->state != PBLK_LINESTATE_GC);
708c2ecf20Sopenharmony_ci	line->state = PBLK_LINESTATE_CLOSED;
718c2ecf20Sopenharmony_ci	trace_pblk_line_state(pblk_disk_name(pblk), line->id,
728c2ecf20Sopenharmony_ci					line->state);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* We need to reset gc_group in order to ensure that
758c2ecf20Sopenharmony_ci	 * pblk_line_gc_list will return proper move_list
768c2ecf20Sopenharmony_ci	 * since right now current line is not on any of the
778c2ecf20Sopenharmony_ci	 * gc lists.
788c2ecf20Sopenharmony_ci	 */
798c2ecf20Sopenharmony_ci	line->gc_group = PBLK_LINEGC_NONE;
808c2ecf20Sopenharmony_ci	move_list = pblk_line_gc_list(pblk, line);
818c2ecf20Sopenharmony_ci	spin_unlock(&line->lock);
828c2ecf20Sopenharmony_ci	list_add_tail(&line->list, move_list);
838c2ecf20Sopenharmony_ci	spin_unlock(&l_mg->gc_lock);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void pblk_gc_line_ws(struct work_struct *work)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct pblk_line_ws *gc_rq_ws = container_of(work,
898c2ecf20Sopenharmony_ci						struct pblk_line_ws, ws);
908c2ecf20Sopenharmony_ci	struct pblk *pblk = gc_rq_ws->pblk;
918c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
928c2ecf20Sopenharmony_ci	struct pblk_line *line = gc_rq_ws->line;
938c2ecf20Sopenharmony_ci	struct pblk_gc_rq *gc_rq = gc_rq_ws->priv;
948c2ecf20Sopenharmony_ci	int ret;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	up(&gc->gc_sem);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* Read from GC victim block */
998c2ecf20Sopenharmony_ci	ret = pblk_submit_read_gc(pblk, gc_rq);
1008c2ecf20Sopenharmony_ci	if (ret) {
1018c2ecf20Sopenharmony_ci		line->w_err_gc->has_gc_err = 1;
1028c2ecf20Sopenharmony_ci		goto out;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (!gc_rq->secs_to_gc)
1068c2ecf20Sopenharmony_ci		goto out;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciretry:
1098c2ecf20Sopenharmony_ci	spin_lock(&gc->w_lock);
1108c2ecf20Sopenharmony_ci	if (gc->w_entries >= PBLK_GC_RQ_QD) {
1118c2ecf20Sopenharmony_ci		spin_unlock(&gc->w_lock);
1128c2ecf20Sopenharmony_ci		pblk_gc_writer_kick(&pblk->gc);
1138c2ecf20Sopenharmony_ci		usleep_range(128, 256);
1148c2ecf20Sopenharmony_ci		goto retry;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	gc->w_entries++;
1178c2ecf20Sopenharmony_ci	list_add_tail(&gc_rq->list, &gc->w_list);
1188c2ecf20Sopenharmony_ci	spin_unlock(&gc->w_lock);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	pblk_gc_writer_kick(&pblk->gc);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	kfree(gc_rq_ws);
1238c2ecf20Sopenharmony_ci	return;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciout:
1268c2ecf20Sopenharmony_ci	pblk_gc_free_gc_rq(gc_rq);
1278c2ecf20Sopenharmony_ci	kref_put(&line->ref, pblk_line_put);
1288c2ecf20Sopenharmony_ci	kfree(gc_rq_ws);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic __le64 *get_lba_list_from_emeta(struct pblk *pblk,
1328c2ecf20Sopenharmony_ci				       struct pblk_line *line)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct line_emeta *emeta_buf;
1358c2ecf20Sopenharmony_ci	struct pblk_line_meta *lm = &pblk->lm;
1368c2ecf20Sopenharmony_ci	unsigned int lba_list_size = lm->emeta_len[2];
1378c2ecf20Sopenharmony_ci	__le64 *lba_list;
1388c2ecf20Sopenharmony_ci	int ret;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	emeta_buf = kvmalloc(lm->emeta_len[0], GFP_KERNEL);
1418c2ecf20Sopenharmony_ci	if (!emeta_buf)
1428c2ecf20Sopenharmony_ci		return NULL;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	ret = pblk_line_emeta_read(pblk, line, emeta_buf);
1458c2ecf20Sopenharmony_ci	if (ret) {
1468c2ecf20Sopenharmony_ci		pblk_err(pblk, "line %d read emeta failed (%d)\n",
1478c2ecf20Sopenharmony_ci				line->id, ret);
1488c2ecf20Sopenharmony_ci		kvfree(emeta_buf);
1498c2ecf20Sopenharmony_ci		return NULL;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* If this read fails, it means that emeta is corrupted.
1538c2ecf20Sopenharmony_ci	 * For now, leave the line untouched.
1548c2ecf20Sopenharmony_ci	 * TODO: Implement a recovery routine that scans and moves
1558c2ecf20Sopenharmony_ci	 * all sectors on the line.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ret = pblk_recov_check_emeta(pblk, emeta_buf);
1598c2ecf20Sopenharmony_ci	if (ret) {
1608c2ecf20Sopenharmony_ci		pblk_err(pblk, "inconsistent emeta (line %d)\n",
1618c2ecf20Sopenharmony_ci				line->id);
1628c2ecf20Sopenharmony_ci		kvfree(emeta_buf);
1638c2ecf20Sopenharmony_ci		return NULL;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	lba_list = kvmalloc(lba_list_size, GFP_KERNEL);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (lba_list)
1698c2ecf20Sopenharmony_ci		memcpy(lba_list, emeta_to_lbas(pblk, emeta_buf), lba_list_size);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	kvfree(emeta_buf);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return lba_list;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void pblk_gc_line_prepare_ws(struct work_struct *work)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct pblk_line_ws *line_ws = container_of(work, struct pblk_line_ws,
1798c2ecf20Sopenharmony_ci									ws);
1808c2ecf20Sopenharmony_ci	struct pblk *pblk = line_ws->pblk;
1818c2ecf20Sopenharmony_ci	struct pblk_line *line = line_ws->line;
1828c2ecf20Sopenharmony_ci	struct pblk_line_meta *lm = &pblk->lm;
1838c2ecf20Sopenharmony_ci	struct nvm_tgt_dev *dev = pblk->dev;
1848c2ecf20Sopenharmony_ci	struct nvm_geo *geo = &dev->geo;
1858c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
1868c2ecf20Sopenharmony_ci	struct pblk_line_ws *gc_rq_ws;
1878c2ecf20Sopenharmony_ci	struct pblk_gc_rq *gc_rq;
1888c2ecf20Sopenharmony_ci	__le64 *lba_list;
1898c2ecf20Sopenharmony_ci	unsigned long *invalid_bitmap;
1908c2ecf20Sopenharmony_ci	int sec_left, nr_secs, bit;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	invalid_bitmap = kmalloc(lm->sec_bitmap_len, GFP_KERNEL);
1938c2ecf20Sopenharmony_ci	if (!invalid_bitmap)
1948c2ecf20Sopenharmony_ci		goto fail_free_ws;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (line->w_err_gc->has_write_err) {
1978c2ecf20Sopenharmony_ci		lba_list = line->w_err_gc->lba_list;
1988c2ecf20Sopenharmony_ci		line->w_err_gc->lba_list = NULL;
1998c2ecf20Sopenharmony_ci	} else {
2008c2ecf20Sopenharmony_ci		lba_list = get_lba_list_from_emeta(pblk, line);
2018c2ecf20Sopenharmony_ci		if (!lba_list) {
2028c2ecf20Sopenharmony_ci			pblk_err(pblk, "could not interpret emeta (line %d)\n",
2038c2ecf20Sopenharmony_ci					line->id);
2048c2ecf20Sopenharmony_ci			goto fail_free_invalid_bitmap;
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	spin_lock(&line->lock);
2098c2ecf20Sopenharmony_ci	bitmap_copy(invalid_bitmap, line->invalid_bitmap, lm->sec_per_line);
2108c2ecf20Sopenharmony_ci	sec_left = pblk_line_vsc(line);
2118c2ecf20Sopenharmony_ci	spin_unlock(&line->lock);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (sec_left < 0) {
2148c2ecf20Sopenharmony_ci		pblk_err(pblk, "corrupted GC line (%d)\n", line->id);
2158c2ecf20Sopenharmony_ci		goto fail_free_lba_list;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	bit = -1;
2198c2ecf20Sopenharmony_cinext_rq:
2208c2ecf20Sopenharmony_ci	gc_rq = kmalloc(sizeof(struct pblk_gc_rq), GFP_KERNEL);
2218c2ecf20Sopenharmony_ci	if (!gc_rq)
2228c2ecf20Sopenharmony_ci		goto fail_free_lba_list;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	nr_secs = 0;
2258c2ecf20Sopenharmony_ci	do {
2268c2ecf20Sopenharmony_ci		bit = find_next_zero_bit(invalid_bitmap, lm->sec_per_line,
2278c2ecf20Sopenharmony_ci								bit + 1);
2288c2ecf20Sopenharmony_ci		if (bit > line->emeta_ssec)
2298c2ecf20Sopenharmony_ci			break;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		gc_rq->paddr_list[nr_secs] = bit;
2328c2ecf20Sopenharmony_ci		gc_rq->lba_list[nr_secs++] = le64_to_cpu(lba_list[bit]);
2338c2ecf20Sopenharmony_ci	} while (nr_secs < pblk->max_write_pgs);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (unlikely(!nr_secs)) {
2368c2ecf20Sopenharmony_ci		kfree(gc_rq);
2378c2ecf20Sopenharmony_ci		goto out;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	gc_rq->nr_secs = nr_secs;
2418c2ecf20Sopenharmony_ci	gc_rq->line = line;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	gc_rq->data = vmalloc(array_size(gc_rq->nr_secs, geo->csecs));
2448c2ecf20Sopenharmony_ci	if (!gc_rq->data)
2458c2ecf20Sopenharmony_ci		goto fail_free_gc_rq;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	gc_rq_ws = kmalloc(sizeof(struct pblk_line_ws), GFP_KERNEL);
2488c2ecf20Sopenharmony_ci	if (!gc_rq_ws)
2498c2ecf20Sopenharmony_ci		goto fail_free_gc_data;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	gc_rq_ws->pblk = pblk;
2528c2ecf20Sopenharmony_ci	gc_rq_ws->line = line;
2538c2ecf20Sopenharmony_ci	gc_rq_ws->priv = gc_rq;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* The write GC path can be much slower than the read GC one due to
2568c2ecf20Sopenharmony_ci	 * the budget imposed by the rate-limiter. Balance in case that we get
2578c2ecf20Sopenharmony_ci	 * back pressure from the write GC path.
2588c2ecf20Sopenharmony_ci	 */
2598c2ecf20Sopenharmony_ci	while (down_timeout(&gc->gc_sem, msecs_to_jiffies(30000)))
2608c2ecf20Sopenharmony_ci		io_schedule();
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	kref_get(&line->ref);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	INIT_WORK(&gc_rq_ws->ws, pblk_gc_line_ws);
2658c2ecf20Sopenharmony_ci	queue_work(gc->gc_line_reader_wq, &gc_rq_ws->ws);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	sec_left -= nr_secs;
2688c2ecf20Sopenharmony_ci	if (sec_left > 0)
2698c2ecf20Sopenharmony_ci		goto next_rq;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciout:
2728c2ecf20Sopenharmony_ci	kvfree(lba_list);
2738c2ecf20Sopenharmony_ci	kfree(line_ws);
2748c2ecf20Sopenharmony_ci	kfree(invalid_bitmap);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	kref_put(&line->ref, pblk_line_put);
2778c2ecf20Sopenharmony_ci	atomic_dec(&gc->read_inflight_gc);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cifail_free_gc_data:
2828c2ecf20Sopenharmony_ci	vfree(gc_rq->data);
2838c2ecf20Sopenharmony_cifail_free_gc_rq:
2848c2ecf20Sopenharmony_ci	kfree(gc_rq);
2858c2ecf20Sopenharmony_cifail_free_lba_list:
2868c2ecf20Sopenharmony_ci	kvfree(lba_list);
2878c2ecf20Sopenharmony_cifail_free_invalid_bitmap:
2888c2ecf20Sopenharmony_ci	kfree(invalid_bitmap);
2898c2ecf20Sopenharmony_cifail_free_ws:
2908c2ecf20Sopenharmony_ci	kfree(line_ws);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* Line goes back to closed state, so we cannot release additional
2938c2ecf20Sopenharmony_ci	 * reference for line, since we do that only when we want to do
2948c2ecf20Sopenharmony_ci	 * gc to free line state transition.
2958c2ecf20Sopenharmony_ci	 */
2968c2ecf20Sopenharmony_ci	pblk_put_line_back(pblk, line);
2978c2ecf20Sopenharmony_ci	atomic_dec(&gc->read_inflight_gc);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	pblk_err(pblk, "failed to GC line %d\n", line->id);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int pblk_gc_line(struct pblk *pblk, struct pblk_line *line)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
3058c2ecf20Sopenharmony_ci	struct pblk_line_ws *line_ws;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	pblk_debug(pblk, "line '%d' being reclaimed for GC\n", line->id);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	line_ws = kmalloc(sizeof(struct pblk_line_ws), GFP_KERNEL);
3108c2ecf20Sopenharmony_ci	if (!line_ws)
3118c2ecf20Sopenharmony_ci		return -ENOMEM;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	line_ws->pblk = pblk;
3148c2ecf20Sopenharmony_ci	line_ws->line = line;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	atomic_inc(&gc->pipeline_gc);
3178c2ecf20Sopenharmony_ci	INIT_WORK(&line_ws->ws, pblk_gc_line_prepare_ws);
3188c2ecf20Sopenharmony_ci	queue_work(gc->gc_reader_wq, &line_ws->ws);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void pblk_gc_reader_kick(struct pblk_gc *gc)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	wake_up_process(gc->gc_reader_ts);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void pblk_gc_kick(struct pblk *pblk)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	pblk_gc_writer_kick(gc);
3338c2ecf20Sopenharmony_ci	pblk_gc_reader_kick(gc);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* If we're shutting down GC, let's not start it up again */
3368c2ecf20Sopenharmony_ci	if (gc->gc_enabled) {
3378c2ecf20Sopenharmony_ci		wake_up_process(gc->gc_ts);
3388c2ecf20Sopenharmony_ci		mod_timer(&gc->gc_timer,
3398c2ecf20Sopenharmony_ci			  jiffies + msecs_to_jiffies(GC_TIME_MSECS));
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int pblk_gc_read(struct pblk *pblk)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
3468c2ecf20Sopenharmony_ci	struct pblk_line *line;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	spin_lock(&gc->r_lock);
3498c2ecf20Sopenharmony_ci	if (list_empty(&gc->r_list)) {
3508c2ecf20Sopenharmony_ci		spin_unlock(&gc->r_lock);
3518c2ecf20Sopenharmony_ci		return 1;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	line = list_first_entry(&gc->r_list, struct pblk_line, list);
3558c2ecf20Sopenharmony_ci	list_del(&line->list);
3568c2ecf20Sopenharmony_ci	spin_unlock(&gc->r_lock);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	pblk_gc_kick(pblk);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (pblk_gc_line(pblk, line)) {
3618c2ecf20Sopenharmony_ci		pblk_err(pblk, "failed to GC line %d\n", line->id);
3628c2ecf20Sopenharmony_ci		/* rollback */
3638c2ecf20Sopenharmony_ci		spin_lock(&gc->r_lock);
3648c2ecf20Sopenharmony_ci		list_add_tail(&line->list, &gc->r_list);
3658c2ecf20Sopenharmony_ci		spin_unlock(&gc->r_lock);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic struct pblk_line *pblk_gc_get_victim_line(struct pblk *pblk,
3728c2ecf20Sopenharmony_ci						 struct list_head *group_list)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct pblk_line *line, *victim;
3758c2ecf20Sopenharmony_ci	unsigned int line_vsc = ~0x0L, victim_vsc = ~0x0L;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	victim = list_first_entry(group_list, struct pblk_line, list);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	list_for_each_entry(line, group_list, list) {
3808c2ecf20Sopenharmony_ci		if (!atomic_read(&line->sec_to_update))
3818c2ecf20Sopenharmony_ci			line_vsc = le32_to_cpu(*line->vsc);
3828c2ecf20Sopenharmony_ci		if (line_vsc < victim_vsc) {
3838c2ecf20Sopenharmony_ci			victim = line;
3848c2ecf20Sopenharmony_ci			victim_vsc = le32_to_cpu(*victim->vsc);
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (victim_vsc == ~0x0)
3898c2ecf20Sopenharmony_ci		return NULL;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return victim;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic bool pblk_gc_should_run(struct pblk_gc *gc, struct pblk_rl *rl)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	unsigned int nr_blocks_free, nr_blocks_need;
3978c2ecf20Sopenharmony_ci	unsigned int werr_lines = atomic_read(&rl->werr_lines);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	nr_blocks_need = pblk_rl_high_thrs(rl);
4008c2ecf20Sopenharmony_ci	nr_blocks_free = pblk_rl_nr_free_blks(rl);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* This is not critical, no need to take lock here */
4038c2ecf20Sopenharmony_ci	return ((werr_lines > 0) ||
4048c2ecf20Sopenharmony_ci		((gc->gc_active) && (nr_blocks_need > nr_blocks_free)));
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_civoid pblk_gc_free_full_lines(struct pblk *pblk)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct pblk_line_mgmt *l_mg = &pblk->l_mg;
4108c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
4118c2ecf20Sopenharmony_ci	struct pblk_line *line;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	do {
4148c2ecf20Sopenharmony_ci		spin_lock(&l_mg->gc_lock);
4158c2ecf20Sopenharmony_ci		if (list_empty(&l_mg->gc_full_list)) {
4168c2ecf20Sopenharmony_ci			spin_unlock(&l_mg->gc_lock);
4178c2ecf20Sopenharmony_ci			return;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		line = list_first_entry(&l_mg->gc_full_list,
4218c2ecf20Sopenharmony_ci							struct pblk_line, list);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		spin_lock(&line->lock);
4248c2ecf20Sopenharmony_ci		WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
4258c2ecf20Sopenharmony_ci		line->state = PBLK_LINESTATE_GC;
4268c2ecf20Sopenharmony_ci		trace_pblk_line_state(pblk_disk_name(pblk), line->id,
4278c2ecf20Sopenharmony_ci					line->state);
4288c2ecf20Sopenharmony_ci		spin_unlock(&line->lock);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		list_del(&line->list);
4318c2ecf20Sopenharmony_ci		spin_unlock(&l_mg->gc_lock);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		atomic_inc(&gc->pipeline_gc);
4348c2ecf20Sopenharmony_ci		kref_put(&line->ref, pblk_line_put);
4358c2ecf20Sopenharmony_ci	} while (1);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/*
4398c2ecf20Sopenharmony_ci * Lines with no valid sectors will be returned to the free list immediately. If
4408c2ecf20Sopenharmony_ci * GC is activated - either because the free block count is under the determined
4418c2ecf20Sopenharmony_ci * threshold, or because it is being forced from user space - only lines with a
4428c2ecf20Sopenharmony_ci * high count of invalid sectors will be recycled.
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic void pblk_gc_run(struct pblk *pblk)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct pblk_line_mgmt *l_mg = &pblk->l_mg;
4478c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
4488c2ecf20Sopenharmony_ci	struct pblk_line *line;
4498c2ecf20Sopenharmony_ci	struct list_head *group_list;
4508c2ecf20Sopenharmony_ci	bool run_gc;
4518c2ecf20Sopenharmony_ci	int read_inflight_gc, gc_group = 0, prev_group = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	pblk_gc_free_full_lines(pblk);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	run_gc = pblk_gc_should_run(&pblk->gc, &pblk->rl);
4568c2ecf20Sopenharmony_ci	if (!run_gc || (atomic_read(&gc->read_inflight_gc) >= PBLK_GC_L_QD))
4578c2ecf20Sopenharmony_ci		return;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cinext_gc_group:
4608c2ecf20Sopenharmony_ci	group_list = l_mg->gc_lists[gc_group++];
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	do {
4638c2ecf20Sopenharmony_ci		spin_lock(&l_mg->gc_lock);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		line = pblk_gc_get_victim_line(pblk, group_list);
4668c2ecf20Sopenharmony_ci		if (!line) {
4678c2ecf20Sopenharmony_ci			spin_unlock(&l_mg->gc_lock);
4688c2ecf20Sopenharmony_ci			break;
4698c2ecf20Sopenharmony_ci		}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		spin_lock(&line->lock);
4728c2ecf20Sopenharmony_ci		WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
4738c2ecf20Sopenharmony_ci		line->state = PBLK_LINESTATE_GC;
4748c2ecf20Sopenharmony_ci		trace_pblk_line_state(pblk_disk_name(pblk), line->id,
4758c2ecf20Sopenharmony_ci					line->state);
4768c2ecf20Sopenharmony_ci		spin_unlock(&line->lock);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		list_del(&line->list);
4798c2ecf20Sopenharmony_ci		spin_unlock(&l_mg->gc_lock);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		spin_lock(&gc->r_lock);
4828c2ecf20Sopenharmony_ci		list_add_tail(&line->list, &gc->r_list);
4838c2ecf20Sopenharmony_ci		spin_unlock(&gc->r_lock);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		read_inflight_gc = atomic_inc_return(&gc->read_inflight_gc);
4868c2ecf20Sopenharmony_ci		pblk_gc_reader_kick(gc);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		prev_group = 1;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/* No need to queue up more GC lines than we can handle */
4918c2ecf20Sopenharmony_ci		run_gc = pblk_gc_should_run(&pblk->gc, &pblk->rl);
4928c2ecf20Sopenharmony_ci		if (!run_gc || read_inflight_gc >= PBLK_GC_L_QD)
4938c2ecf20Sopenharmony_ci			break;
4948c2ecf20Sopenharmony_ci	} while (1);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (!prev_group && pblk->rl.rb_state > gc_group &&
4978c2ecf20Sopenharmony_ci						gc_group < PBLK_GC_NR_LISTS)
4988c2ecf20Sopenharmony_ci		goto next_gc_group;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void pblk_gc_timer(struct timer_list *t)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct pblk *pblk = from_timer(pblk, t, gc.gc_timer);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	pblk_gc_kick(pblk);
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic int pblk_gc_ts(void *data)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct pblk *pblk = data;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
5138c2ecf20Sopenharmony_ci		pblk_gc_run(pblk);
5148c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
5158c2ecf20Sopenharmony_ci		io_schedule();
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return 0;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int pblk_gc_writer_ts(void *data)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct pblk *pblk = data;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
5268c2ecf20Sopenharmony_ci		if (!pblk_gc_write(pblk))
5278c2ecf20Sopenharmony_ci			continue;
5288c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
5298c2ecf20Sopenharmony_ci		io_schedule();
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return 0;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic int pblk_gc_reader_ts(void *data)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct pblk *pblk = data;
5388c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
5418c2ecf20Sopenharmony_ci		if (!pblk_gc_read(pblk))
5428c2ecf20Sopenharmony_ci			continue;
5438c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
5448c2ecf20Sopenharmony_ci		io_schedule();
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci#ifdef CONFIG_NVM_PBLK_DEBUG
5488c2ecf20Sopenharmony_ci	pblk_info(pblk, "flushing gc pipeline, %d lines left\n",
5498c2ecf20Sopenharmony_ci		atomic_read(&gc->pipeline_gc));
5508c2ecf20Sopenharmony_ci#endif
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	do {
5538c2ecf20Sopenharmony_ci		if (!atomic_read(&gc->pipeline_gc))
5548c2ecf20Sopenharmony_ci			break;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		schedule();
5578c2ecf20Sopenharmony_ci	} while (1);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return 0;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void pblk_gc_start(struct pblk *pblk)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	pblk->gc.gc_active = 1;
5658c2ecf20Sopenharmony_ci	pblk_debug(pblk, "gc start\n");
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_civoid pblk_gc_should_start(struct pblk *pblk)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (gc->gc_enabled && !gc->gc_active) {
5738c2ecf20Sopenharmony_ci		pblk_gc_start(pblk);
5748c2ecf20Sopenharmony_ci		pblk_gc_kick(pblk);
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_civoid pblk_gc_should_stop(struct pblk *pblk)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (gc->gc_active && !gc->gc_forced)
5838c2ecf20Sopenharmony_ci		gc->gc_active = 0;
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_civoid pblk_gc_should_kick(struct pblk *pblk)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	pblk_rl_update_rates(&pblk->rl);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_civoid pblk_gc_sysfs_state_show(struct pblk *pblk, int *gc_enabled,
5928c2ecf20Sopenharmony_ci			      int *gc_active)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	spin_lock(&gc->lock);
5978c2ecf20Sopenharmony_ci	*gc_enabled = gc->gc_enabled;
5988c2ecf20Sopenharmony_ci	*gc_active = gc->gc_active;
5998c2ecf20Sopenharmony_ci	spin_unlock(&gc->lock);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ciint pblk_gc_sysfs_force(struct pblk *pblk, int force)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (force < 0 || force > 1)
6078c2ecf20Sopenharmony_ci		return -EINVAL;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	spin_lock(&gc->lock);
6108c2ecf20Sopenharmony_ci	gc->gc_forced = force;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (force)
6138c2ecf20Sopenharmony_ci		gc->gc_enabled = 1;
6148c2ecf20Sopenharmony_ci	else
6158c2ecf20Sopenharmony_ci		gc->gc_enabled = 0;
6168c2ecf20Sopenharmony_ci	spin_unlock(&gc->lock);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	pblk_gc_should_start(pblk);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	return 0;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ciint pblk_gc_init(struct pblk *pblk)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
6268c2ecf20Sopenharmony_ci	int ret;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	gc->gc_ts = kthread_create(pblk_gc_ts, pblk, "pblk-gc-ts");
6298c2ecf20Sopenharmony_ci	if (IS_ERR(gc->gc_ts)) {
6308c2ecf20Sopenharmony_ci		pblk_err(pblk, "could not allocate GC main kthread\n");
6318c2ecf20Sopenharmony_ci		return PTR_ERR(gc->gc_ts);
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	gc->gc_writer_ts = kthread_create(pblk_gc_writer_ts, pblk,
6358c2ecf20Sopenharmony_ci							"pblk-gc-writer-ts");
6368c2ecf20Sopenharmony_ci	if (IS_ERR(gc->gc_writer_ts)) {
6378c2ecf20Sopenharmony_ci		pblk_err(pblk, "could not allocate GC writer kthread\n");
6388c2ecf20Sopenharmony_ci		ret = PTR_ERR(gc->gc_writer_ts);
6398c2ecf20Sopenharmony_ci		goto fail_free_main_kthread;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	gc->gc_reader_ts = kthread_create(pblk_gc_reader_ts, pblk,
6438c2ecf20Sopenharmony_ci							"pblk-gc-reader-ts");
6448c2ecf20Sopenharmony_ci	if (IS_ERR(gc->gc_reader_ts)) {
6458c2ecf20Sopenharmony_ci		pblk_err(pblk, "could not allocate GC reader kthread\n");
6468c2ecf20Sopenharmony_ci		ret = PTR_ERR(gc->gc_reader_ts);
6478c2ecf20Sopenharmony_ci		goto fail_free_writer_kthread;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	timer_setup(&gc->gc_timer, pblk_gc_timer, 0);
6518c2ecf20Sopenharmony_ci	mod_timer(&gc->gc_timer, jiffies + msecs_to_jiffies(GC_TIME_MSECS));
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	gc->gc_active = 0;
6548c2ecf20Sopenharmony_ci	gc->gc_forced = 0;
6558c2ecf20Sopenharmony_ci	gc->gc_enabled = 1;
6568c2ecf20Sopenharmony_ci	gc->w_entries = 0;
6578c2ecf20Sopenharmony_ci	atomic_set(&gc->read_inflight_gc, 0);
6588c2ecf20Sopenharmony_ci	atomic_set(&gc->pipeline_gc, 0);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/* Workqueue that reads valid sectors from a line and submit them to the
6618c2ecf20Sopenharmony_ci	 * GC writer to be recycled.
6628c2ecf20Sopenharmony_ci	 */
6638c2ecf20Sopenharmony_ci	gc->gc_line_reader_wq = alloc_workqueue("pblk-gc-line-reader-wq",
6648c2ecf20Sopenharmony_ci			WQ_MEM_RECLAIM | WQ_UNBOUND, PBLK_GC_MAX_READERS);
6658c2ecf20Sopenharmony_ci	if (!gc->gc_line_reader_wq) {
6668c2ecf20Sopenharmony_ci		pblk_err(pblk, "could not allocate GC line reader workqueue\n");
6678c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6688c2ecf20Sopenharmony_ci		goto fail_free_reader_kthread;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* Workqueue that prepare lines for GC */
6728c2ecf20Sopenharmony_ci	gc->gc_reader_wq = alloc_workqueue("pblk-gc-line_wq",
6738c2ecf20Sopenharmony_ci					WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
6748c2ecf20Sopenharmony_ci	if (!gc->gc_reader_wq) {
6758c2ecf20Sopenharmony_ci		pblk_err(pblk, "could not allocate GC reader workqueue\n");
6768c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6778c2ecf20Sopenharmony_ci		goto fail_free_reader_line_wq;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	spin_lock_init(&gc->lock);
6818c2ecf20Sopenharmony_ci	spin_lock_init(&gc->w_lock);
6828c2ecf20Sopenharmony_ci	spin_lock_init(&gc->r_lock);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	sema_init(&gc->gc_sem, PBLK_GC_RQ_QD);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gc->w_list);
6878c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gc->r_list);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return 0;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cifail_free_reader_line_wq:
6928c2ecf20Sopenharmony_ci	destroy_workqueue(gc->gc_line_reader_wq);
6938c2ecf20Sopenharmony_cifail_free_reader_kthread:
6948c2ecf20Sopenharmony_ci	kthread_stop(gc->gc_reader_ts);
6958c2ecf20Sopenharmony_cifail_free_writer_kthread:
6968c2ecf20Sopenharmony_ci	kthread_stop(gc->gc_writer_ts);
6978c2ecf20Sopenharmony_cifail_free_main_kthread:
6988c2ecf20Sopenharmony_ci	kthread_stop(gc->gc_ts);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	return ret;
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_civoid pblk_gc_exit(struct pblk *pblk, bool graceful)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct pblk_gc *gc = &pblk->gc;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	gc->gc_enabled = 0;
7088c2ecf20Sopenharmony_ci	del_timer_sync(&gc->gc_timer);
7098c2ecf20Sopenharmony_ci	gc->gc_active = 0;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (gc->gc_ts)
7128c2ecf20Sopenharmony_ci		kthread_stop(gc->gc_ts);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (gc->gc_reader_ts)
7158c2ecf20Sopenharmony_ci		kthread_stop(gc->gc_reader_ts);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (graceful) {
7188c2ecf20Sopenharmony_ci		flush_workqueue(gc->gc_reader_wq);
7198c2ecf20Sopenharmony_ci		flush_workqueue(gc->gc_line_reader_wq);
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	destroy_workqueue(gc->gc_reader_wq);
7238c2ecf20Sopenharmony_ci	destroy_workqueue(gc->gc_line_reader_wq);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (gc->gc_writer_ts)
7268c2ecf20Sopenharmony_ci		kthread_stop(gc->gc_writer_ts);
7278c2ecf20Sopenharmony_ci}
728