Blame view

drivers/lightnvm/pblk-gc.c 14.6 KB
a4bd217b4   Javier González   lightnvm: physica...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * Copyright (C) 2016 CNEX Labs
   * Initial release: Javier Gonzalez <javier@cnexlabs.com>
   *                  Matias Bjorling <matias@cnexlabs.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License version
   * 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * pblk-gc.c - pblk's garbage collector
   */
  
  #include "pblk.h"
  #include <linux/delay.h>
  
  static void pblk_gc_free_gc_rq(struct pblk_gc_rq *gc_rq)
  {
de54e703a   Javier González   lightnvm: pblk: u...
23
  	vfree(gc_rq->data);
a4bd217b4   Javier González   lightnvm: physica...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  	kfree(gc_rq);
  }
  
  static int pblk_gc_write(struct pblk *pblk)
  {
  	struct pblk_gc *gc = &pblk->gc;
  	struct pblk_gc_rq *gc_rq, *tgc_rq;
  	LIST_HEAD(w_list);
  
  	spin_lock(&gc->w_lock);
  	if (list_empty(&gc->w_list)) {
  		spin_unlock(&gc->w_lock);
  		return 1;
  	}
b20ba1bc7   Javier González   lightnvm: pblk: r...
38
39
  	list_cut_position(&w_list, &gc->w_list, gc->w_list.prev);
  	gc->w_entries = 0;
a4bd217b4   Javier González   lightnvm: physica...
40
41
42
43
44
45
  	spin_unlock(&gc->w_lock);
  
  	list_for_each_entry_safe(gc_rq, tgc_rq, &w_list, list) {
  		pblk_write_gc_to_cache(pblk, gc_rq->data, gc_rq->lba_list,
  				gc_rq->nr_secs, gc_rq->secs_to_gc,
  				gc_rq->line, PBLK_IOTYPE_GC);
a4bd217b4   Javier González   lightnvm: physica...
46
  		list_del(&gc_rq->list);
b20ba1bc7   Javier González   lightnvm: pblk: r...
47
  		kref_put(&gc_rq->line->ref, pblk_line_put);
a4bd217b4   Javier González   lightnvm: physica...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  		pblk_gc_free_gc_rq(gc_rq);
  	}
  
  	return 0;
  }
  
  static void pblk_gc_writer_kick(struct pblk_gc *gc)
  {
  	wake_up_process(gc->gc_writer_ts);
  }
  
  /*
   * Responsible for managing all memory related to a gc request. Also in case of
   * failure
   */
b20ba1bc7   Javier González   lightnvm: pblk: r...
63
  static int pblk_gc_move_valid_secs(struct pblk *pblk, struct pblk_gc_rq *gc_rq)
a4bd217b4   Javier González   lightnvm: physica...
64
65
66
67
  {
  	struct nvm_tgt_dev *dev = pblk->dev;
  	struct nvm_geo *geo = &dev->geo;
  	struct pblk_gc *gc = &pblk->gc;
b20ba1bc7   Javier González   lightnvm: pblk: r...
68
  	struct pblk_line *line = gc_rq->line;
a4bd217b4   Javier González   lightnvm: physica...
69
70
  	void *data;
  	unsigned int secs_to_gc;
b20ba1bc7   Javier González   lightnvm: pblk: r...
71
  	int ret = 0;
a4bd217b4   Javier González   lightnvm: physica...
72

de54e703a   Javier González   lightnvm: pblk: u...
73
  	data = vmalloc(gc_rq->nr_secs * geo->sec_size);
a4bd217b4   Javier González   lightnvm: physica...
74
  	if (!data) {
b20ba1bc7   Javier González   lightnvm: pblk: r...
75
76
  		ret = -ENOMEM;
  		goto out;
a4bd217b4   Javier González   lightnvm: physica...
77
78
79
  	}
  
  	/* Read from GC victim block */
b20ba1bc7   Javier González   lightnvm: pblk: r...
80
  	if (pblk_submit_read_gc(pblk, gc_rq->lba_list, data, gc_rq->nr_secs,
a4bd217b4   Javier González   lightnvm: physica...
81
  							&secs_to_gc, line)) {
b20ba1bc7   Javier González   lightnvm: pblk: r...
82
  		ret = -EFAULT;
a4bd217b4   Javier González   lightnvm: physica...
83
84
85
86
  		goto free_data;
  	}
  
  	if (!secs_to_gc)
b20ba1bc7   Javier González   lightnvm: pblk: r...
87
  		goto free_rq;
a4bd217b4   Javier González   lightnvm: physica...
88

a4bd217b4   Javier González   lightnvm: physica...
89
  	gc_rq->data = data;
a4bd217b4   Javier González   lightnvm: physica...
90
  	gc_rq->secs_to_gc = secs_to_gc;
a4bd217b4   Javier González   lightnvm: physica...
91
92
  retry:
  	spin_lock(&gc->w_lock);
b20ba1bc7   Javier González   lightnvm: pblk: r...
93
  	if (gc->w_entries >= PBLK_GC_W_QD) {
a4bd217b4   Javier González   lightnvm: physica...
94
  		spin_unlock(&gc->w_lock);
b20ba1bc7   Javier González   lightnvm: pblk: r...
95
96
  		pblk_gc_writer_kick(&pblk->gc);
  		usleep_range(128, 256);
a4bd217b4   Javier González   lightnvm: physica...
97
98
99
100
101
102
103
  		goto retry;
  	}
  	gc->w_entries++;
  	list_add_tail(&gc_rq->list, &gc->w_list);
  	spin_unlock(&gc->w_lock);
  
  	pblk_gc_writer_kick(&pblk->gc);
b20ba1bc7   Javier González   lightnvm: pblk: r...
104
  	return 0;
a4bd217b4   Javier González   lightnvm: physica...
105

b20ba1bc7   Javier González   lightnvm: pblk: r...
106
107
  free_rq:
  	kfree(gc_rq);
a4bd217b4   Javier González   lightnvm: physica...
108
  free_data:
de54e703a   Javier González   lightnvm: pblk: u...
109
  	vfree(data);
b20ba1bc7   Javier González   lightnvm: pblk: r...
110
111
  out:
  	kref_put(&line->ref, pblk_line_put);
a4bd217b4   Javier González   lightnvm: physica...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  	return ret;
  }
  
  static 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(&line->lock);
  	WARN_ON(line->state != PBLK_LINESTATE_GC);
  	line->state = PBLK_LINESTATE_CLOSED;
  	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);
  	}
  }
  
  static void pblk_gc_line_ws(struct work_struct *work)
  {
b20ba1bc7   Javier González   lightnvm: pblk: r...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  	struct pblk_line_ws *line_rq_ws = container_of(work,
  						struct pblk_line_ws, ws);
  	struct pblk *pblk = line_rq_ws->pblk;
  	struct pblk_gc *gc = &pblk->gc;
  	struct pblk_line *line = line_rq_ws->line;
  	struct pblk_gc_rq *gc_rq = line_rq_ws->priv;
  
  	up(&gc->gc_sem);
  
  	if (pblk_gc_move_valid_secs(pblk, gc_rq)) {
  		pr_err("pblk: could not GC all sectors: line:%d (%d/%d)
  ",
  						line->id, *line->vsc,
  						gc_rq->nr_secs);
  	}
  
  	mempool_free(line_rq_ws, pblk->line_ws_pool);
  }
  
  static void pblk_gc_line_prepare_ws(struct work_struct *work)
  {
a4bd217b4   Javier González   lightnvm: physica...
156
157
158
  	struct pblk_line_ws *line_ws = container_of(work, struct pblk_line_ws,
  									ws);
  	struct pblk *pblk = line_ws->pblk;
a4bd217b4   Javier González   lightnvm: physica...
159
  	struct pblk_line *line = line_ws->line;
b20ba1bc7   Javier González   lightnvm: pblk: r...
160
  	struct pblk_line_mgmt *l_mg = &pblk->l_mg;
a4bd217b4   Javier González   lightnvm: physica...
161
  	struct pblk_line_meta *lm = &pblk->lm;
b20ba1bc7   Javier González   lightnvm: pblk: r...
162
163
164
165
  	struct pblk_gc *gc = &pblk->gc;
  	struct line_emeta *emeta_buf;
  	struct pblk_line_ws *line_rq_ws;
  	struct pblk_gc_rq *gc_rq;
dd2a43437   Javier González   lightnvm: pblk: s...
166
  	__le64 *lba_list;
b20ba1bc7   Javier González   lightnvm: pblk: r...
167
168
  	int sec_left, nr_secs, bit;
  	int ret;
a4bd217b4   Javier González   lightnvm: physica...
169

b20ba1bc7   Javier González   lightnvm: pblk: r...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  	emeta_buf = pblk_malloc(lm->emeta_len[0], l_mg->emeta_alloc_type,
  								GFP_KERNEL);
  	if (!emeta_buf) {
  		pr_err("pblk: cannot use GC emeta
  ");
  		return;
  	}
  
  	ret = pblk_line_read_emeta(pblk, line, emeta_buf);
  	if (ret) {
  		pr_err("pblk: line %d read emeta failed (%d)
  ", line->id, ret);
  		goto fail_free_emeta;
  	}
a4bd217b4   Javier González   lightnvm: physica...
184

dd2a43437   Javier González   lightnvm: pblk: s...
185
186
187
188
189
190
191
192
  	/* If this read fails, it means that emeta is corrupted. For now, leave
  	 * the line untouched. TODO: Implement a recovery routine that scans and
  	 * moves all sectors on the line.
  	 */
  	lba_list = pblk_recov_get_lba_list(pblk, emeta_buf);
  	if (!lba_list) {
  		pr_err("pblk: could not interpret emeta (line %d)
  ", line->id);
b20ba1bc7   Javier González   lightnvm: pblk: r...
193
  		goto fail_free_emeta;
a4bd217b4   Javier González   lightnvm: physica...
194
  	}
a4bd217b4   Javier González   lightnvm: physica...
195

b20ba1bc7   Javier González   lightnvm: pblk: r...
196
  	sec_left = pblk_line_vsc(line);
a4bd217b4   Javier González   lightnvm: physica...
197
198
199
  	if (sec_left < 0) {
  		pr_err("pblk: corrupted GC line (%d)
  ", line->id);
b20ba1bc7   Javier González   lightnvm: pblk: r...
200
  		goto fail_free_emeta;
a4bd217b4   Javier González   lightnvm: physica...
201
202
203
204
  	}
  
  	bit = -1;
  next_rq:
b20ba1bc7   Javier González   lightnvm: pblk: r...
205
206
207
  	gc_rq = kmalloc(sizeof(struct pblk_gc_rq), GFP_KERNEL);
  	if (!gc_rq)
  		goto fail_free_emeta;
a4bd217b4   Javier González   lightnvm: physica...
208

b20ba1bc7   Javier González   lightnvm: pblk: r...
209
  	nr_secs = 0;
a4bd217b4   Javier González   lightnvm: physica...
210
211
212
213
214
  	do {
  		bit = find_next_zero_bit(line->invalid_bitmap, lm->sec_per_line,
  								bit + 1);
  		if (bit > line->emeta_ssec)
  			break;
b20ba1bc7   Javier González   lightnvm: pblk: r...
215
216
  		gc_rq->lba_list[nr_secs++] = le64_to_cpu(lba_list[bit]);
  	} while (nr_secs < pblk->max_write_pgs);
a4bd217b4   Javier González   lightnvm: physica...
217

b20ba1bc7   Javier González   lightnvm: pblk: r...
218
219
  	if (unlikely(!nr_secs)) {
  		kfree(gc_rq);
a4bd217b4   Javier González   lightnvm: physica...
220
221
  		goto out;
  	}
b20ba1bc7   Javier González   lightnvm: pblk: r...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	gc_rq->nr_secs = nr_secs;
  	gc_rq->line = line;
  
  	line_rq_ws = mempool_alloc(pblk->line_ws_pool, GFP_KERNEL);
  	if (!line_rq_ws)
  		goto fail_free_gc_rq;
  
  	line_rq_ws->pblk = pblk;
  	line_rq_ws->line = line;
  	line_rq_ws->priv = gc_rq;
  
  	down(&gc->gc_sem);
  	kref_get(&line->ref);
  
  	INIT_WORK(&line_rq_ws->ws, pblk_gc_line_ws);
  	queue_work(gc->gc_line_reader_wq, &line_rq_ws->ws);
a4bd217b4   Javier González   lightnvm: physica...
238

b20ba1bc7   Javier González   lightnvm: pblk: r...
239
  	sec_left -= nr_secs;
a4bd217b4   Javier González   lightnvm: physica...
240
241
242
243
  	if (sec_left > 0)
  		goto next_rq;
  
  out:
dd2a43437   Javier González   lightnvm: pblk: s...
244
  	pblk_mfree(emeta_buf, l_mg->emeta_alloc_type);
a4bd217b4   Javier González   lightnvm: physica...
245
  	mempool_free(line_ws, pblk->line_ws_pool);
b20ba1bc7   Javier González   lightnvm: pblk: r...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  
  	kref_put(&line->ref, pblk_line_put);
  	atomic_dec(&gc->inflight_gc);
  
  	return;
  
  fail_free_gc_rq:
  	kfree(gc_rq);
  fail_free_emeta:
  	pblk_mfree(emeta_buf, l_mg->emeta_alloc_type);
  	pblk_put_line_back(pblk, line);
  	kref_put(&line->ref, pblk_line_put);
  	mempool_free(line_ws, pblk->line_ws_pool);
  	atomic_dec(&gc->inflight_gc);
  
  	pr_err("pblk: Failed to GC line %d
  ", line->id);
a4bd217b4   Javier González   lightnvm: physica...
263
264
265
266
  }
  
  static int pblk_gc_line(struct pblk *pblk, struct pblk_line *line)
  {
b20ba1bc7   Javier González   lightnvm: pblk: r...
267
  	struct pblk_gc *gc = &pblk->gc;
a4bd217b4   Javier González   lightnvm: physica...
268
  	struct pblk_line_ws *line_ws;
a4bd217b4   Javier González   lightnvm: physica...
269

b20ba1bc7   Javier González   lightnvm: pblk: r...
270
271
  	pr_debug("pblk: line '%d' being reclaimed for GC
  ", line->id);
a4bd217b4   Javier González   lightnvm: physica...
272

b20ba1bc7   Javier González   lightnvm: pblk: r...
273
274
275
  	line_ws = mempool_alloc(pblk->line_ws_pool, GFP_KERNEL);
  	if (!line_ws)
  		return -ENOMEM;
a4bd217b4   Javier González   lightnvm: physica...
276

a4bd217b4   Javier González   lightnvm: physica...
277
278
  	line_ws->pblk = pblk;
  	line_ws->line = line;
a4bd217b4   Javier González   lightnvm: physica...
279

b20ba1bc7   Javier González   lightnvm: pblk: r...
280
281
  	INIT_WORK(&line_ws->ws, pblk_gc_line_prepare_ws);
  	queue_work(gc->gc_reader_wq, &line_ws->ws);
a4bd217b4   Javier González   lightnvm: physica...
282
283
  
  	return 0;
a4bd217b4   Javier González   lightnvm: physica...
284
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
285
  static int pblk_gc_read(struct pblk *pblk)
a4bd217b4   Javier González   lightnvm: physica...
286
  {
b20ba1bc7   Javier González   lightnvm: pblk: r...
287
288
  	struct pblk_gc *gc = &pblk->gc;
  	struct pblk_line *line;
a4bd217b4   Javier González   lightnvm: physica...
289

b20ba1bc7   Javier González   lightnvm: pblk: r...
290
291
292
293
  	spin_lock(&gc->r_lock);
  	if (list_empty(&gc->r_list)) {
  		spin_unlock(&gc->r_lock);
  		return 1;
a4bd217b4   Javier González   lightnvm: physica...
294
  	}
b20ba1bc7   Javier González   lightnvm: pblk: r...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  
  	line = list_first_entry(&gc->r_list, struct pblk_line, list);
  	list_del(&line->list);
  	spin_unlock(&gc->r_lock);
  
  	pblk_gc_kick(pblk);
  
  	if (pblk_gc_line(pblk, line))
  		pr_err("pblk: failed to GC line %d
  ", line->id);
  
  	return 0;
  }
  
  static void pblk_gc_reader_kick(struct pblk_gc *gc)
  {
  	wake_up_process(gc->gc_reader_ts);
a4bd217b4   Javier González   lightnvm: physica...
312
  }
d45ebd470   Javier González   lightnvm: pblk: c...
313
314
315
316
  static struct pblk_line *pblk_gc_get_victim_line(struct pblk *pblk,
  						 struct list_head *group_list)
  {
  	struct pblk_line *line, *victim;
f417aa0bd   Javier González   lightnvm: pblk: f...
317
  	int line_vsc, victim_vsc;
d45ebd470   Javier González   lightnvm: pblk: c...
318
319
320
  
  	victim = list_first_entry(group_list, struct pblk_line, list);
  	list_for_each_entry(line, group_list, list) {
f417aa0bd   Javier González   lightnvm: pblk: f...
321
322
323
  		line_vsc = le32_to_cpu(*line->vsc);
  		victim_vsc = le32_to_cpu(*victim->vsc);
  		if (line_vsc < victim_vsc)
d45ebd470   Javier González   lightnvm: pblk: c...
324
325
326
327
328
  			victim = line;
  	}
  
  	return victim;
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
329
330
331
332
333
334
335
336
337
338
  static bool pblk_gc_should_run(struct pblk_gc *gc, struct pblk_rl *rl)
  {
  	unsigned int nr_blocks_free, nr_blocks_need;
  
  	nr_blocks_need = pblk_rl_high_thrs(rl);
  	nr_blocks_free = pblk_rl_nr_free_blks(rl);
  
  	/* This is not critical, no need to take lock here */
  	return ((gc->gc_active) && (nr_blocks_need > nr_blocks_free));
  }
a4bd217b4   Javier González   lightnvm: physica...
339
340
341
342
343
344
345
346
347
348
  /*
   * Lines with no valid sectors will be returned to the free list immediately. If
   * GC is activated - either because the free block count is under the determined
   * threshold, or because it is being forced from user space - only lines with a
   * high count of invalid sectors will be recycled.
   */
  static void pblk_gc_run(struct pblk *pblk)
  {
  	struct pblk_line_mgmt *l_mg = &pblk->l_mg;
  	struct pblk_gc *gc = &pblk->gc;
b20ba1bc7   Javier González   lightnvm: pblk: r...
349
  	struct pblk_line *line;
a4bd217b4   Javier González   lightnvm: physica...
350
  	struct list_head *group_list;
b20ba1bc7   Javier González   lightnvm: pblk: r...
351
352
353
354
355
356
357
358
359
360
361
362
  	bool run_gc;
  	int inflight_gc, gc_group = 0, prev_group = 0;
  
  	do {
  		spin_lock(&l_mg->gc_lock);
  		if (list_empty(&l_mg->gc_full_list)) {
  			spin_unlock(&l_mg->gc_lock);
  			break;
  		}
  
  		line = list_first_entry(&l_mg->gc_full_list,
  							struct pblk_line, list);
a4bd217b4   Javier González   lightnvm: physica...
363

a4bd217b4   Javier González   lightnvm: physica...
364
365
366
367
368
369
  		spin_lock(&line->lock);
  		WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
  		line->state = PBLK_LINESTATE_GC;
  		spin_unlock(&line->lock);
  
  		list_del(&line->list);
b20ba1bc7   Javier González   lightnvm: pblk: r...
370
  		spin_unlock(&l_mg->gc_lock);
a4bd217b4   Javier González   lightnvm: physica...
371
  		kref_put(&line->ref, pblk_line_put);
b20ba1bc7   Javier González   lightnvm: pblk: r...
372
  	} while (1);
a4bd217b4   Javier González   lightnvm: physica...
373

b20ba1bc7   Javier González   lightnvm: pblk: r...
374
375
376
  	run_gc = pblk_gc_should_run(&pblk->gc, &pblk->rl);
  	if (!run_gc || (atomic_read(&gc->inflight_gc) >= PBLK_GC_L_QD))
  		return;
a4bd217b4   Javier González   lightnvm: physica...
377
378
379
  
  next_gc_group:
  	group_list = l_mg->gc_lists[gc_group++];
b20ba1bc7   Javier González   lightnvm: pblk: r...
380
381
382
383
  
  	do {
  		spin_lock(&l_mg->gc_lock);
  		if (list_empty(group_list)) {
a4bd217b4   Javier González   lightnvm: physica...
384
  			spin_unlock(&l_mg->gc_lock);
b20ba1bc7   Javier González   lightnvm: pblk: r...
385
  			break;
a4bd217b4   Javier González   lightnvm: physica...
386
  		}
d45ebd470   Javier González   lightnvm: pblk: c...
387
  		line = pblk_gc_get_victim_line(pblk, group_list);
a4bd217b4   Javier González   lightnvm: physica...
388
389
390
391
  
  		spin_lock(&line->lock);
  		WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
  		line->state = PBLK_LINESTATE_GC;
a4bd217b4   Javier González   lightnvm: physica...
392
  		spin_unlock(&line->lock);
b20ba1bc7   Javier González   lightnvm: pblk: r...
393
394
395
396
397
398
399
400
401
  		list_del(&line->list);
  		spin_unlock(&l_mg->gc_lock);
  
  		spin_lock(&gc->r_lock);
  		list_add_tail(&line->list, &gc->r_list);
  		spin_unlock(&gc->r_lock);
  
  		inflight_gc = atomic_inc_return(&gc->inflight_gc);
  		pblk_gc_reader_kick(gc);
a4bd217b4   Javier González   lightnvm: physica...
402

b20ba1bc7   Javier González   lightnvm: pblk: r...
403
  		prev_group = 1;
a4bd217b4   Javier González   lightnvm: physica...
404

b20ba1bc7   Javier González   lightnvm: pblk: r...
405
406
407
408
409
410
411
412
  		/* No need to queue up more GC lines than we can handle */
  		run_gc = pblk_gc_should_run(&pblk->gc, &pblk->rl);
  		if (!run_gc || inflight_gc >= PBLK_GC_L_QD)
  			break;
  	} while (1);
  
  	if (!prev_group && pblk->rl.rb_state > gc_group &&
  						gc_group < PBLK_GC_NR_LISTS)
a4bd217b4   Javier González   lightnvm: physica...
413
414
  		goto next_gc_group;
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
415
  void pblk_gc_kick(struct pblk *pblk)
a4bd217b4   Javier González   lightnvm: physica...
416
417
418
419
420
  {
  	struct pblk_gc *gc = &pblk->gc;
  
  	wake_up_process(gc->gc_ts);
  	pblk_gc_writer_kick(gc);
b20ba1bc7   Javier González   lightnvm: pblk: r...
421
  	pblk_gc_reader_kick(gc);
a4bd217b4   Javier González   lightnvm: physica...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
  	mod_timer(&gc->gc_timer, jiffies + msecs_to_jiffies(GC_TIME_MSECS));
  }
  
  static void pblk_gc_timer(unsigned long data)
  {
  	struct pblk *pblk = (struct pblk *)data;
  
  	pblk_gc_kick(pblk);
  }
  
  static int pblk_gc_ts(void *data)
  {
  	struct pblk *pblk = data;
  
  	while (!kthread_should_stop()) {
  		pblk_gc_run(pblk);
  		set_current_state(TASK_INTERRUPTIBLE);
  		io_schedule();
  	}
  
  	return 0;
  }
  
  static int pblk_gc_writer_ts(void *data)
  {
  	struct pblk *pblk = data;
  
  	while (!kthread_should_stop()) {
  		if (!pblk_gc_write(pblk))
  			continue;
  		set_current_state(TASK_INTERRUPTIBLE);
  		io_schedule();
  	}
  
  	return 0;
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
458
  static int pblk_gc_reader_ts(void *data)
a4bd217b4   Javier González   lightnvm: physica...
459
  {
b20ba1bc7   Javier González   lightnvm: pblk: r...
460
  	struct pblk *pblk = data;
a4bd217b4   Javier González   lightnvm: physica...
461

b20ba1bc7   Javier González   lightnvm: pblk: r...
462
463
464
465
466
467
468
469
  	while (!kthread_should_stop()) {
  		if (!pblk_gc_read(pblk))
  			continue;
  		set_current_state(TASK_INTERRUPTIBLE);
  		io_schedule();
  	}
  
  	return 0;
a4bd217b4   Javier González   lightnvm: physica...
470
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
471
  static void pblk_gc_start(struct pblk *pblk)
a4bd217b4   Javier González   lightnvm: physica...
472
  {
b20ba1bc7   Javier González   lightnvm: pblk: r...
473
474
475
  	pblk->gc.gc_active = 1;
  	pr_debug("pblk: gc start
  ");
a4bd217b4   Javier González   lightnvm: physica...
476
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
477
  void pblk_gc_should_start(struct pblk *pblk)
a4bd217b4   Javier González   lightnvm: physica...
478
479
  {
  	struct pblk_gc *gc = &pblk->gc;
1b07f7511   Hans Holmberg   lightnvm: pblk: p...
480
  	if (gc->gc_enabled && !gc->gc_active) {
a4bd217b4   Javier González   lightnvm: physica...
481
  		pblk_gc_start(pblk);
1b07f7511   Hans Holmberg   lightnvm: pblk: p...
482
483
  		pblk_gc_kick(pblk);
  	}
a4bd217b4   Javier González   lightnvm: physica...
484
485
486
487
488
489
490
491
  }
  
  /*
   * If flush_wq == 1 then no lock should be held by the caller since
   * flush_workqueue can sleep
   */
  static void pblk_gc_stop(struct pblk *pblk, int flush_wq)
  {
a4bd217b4   Javier González   lightnvm: physica...
492
  	pblk->gc.gc_active = 0;
a4bd217b4   Javier González   lightnvm: physica...
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
  	pr_debug("pblk: gc stop
  ");
  }
  
  void pblk_gc_should_stop(struct pblk *pblk)
  {
  	struct pblk_gc *gc = &pblk->gc;
  
  	if (gc->gc_active && !gc->gc_forced)
  		pblk_gc_stop(pblk, 0);
  }
  
  void pblk_gc_sysfs_state_show(struct pblk *pblk, int *gc_enabled,
  			      int *gc_active)
  {
  	struct pblk_gc *gc = &pblk->gc;
  
  	spin_lock(&gc->lock);
  	*gc_enabled = gc->gc_enabled;
  	*gc_active = gc->gc_active;
  	spin_unlock(&gc->lock);
  }
b20ba1bc7   Javier González   lightnvm: pblk: r...
515
  int pblk_gc_sysfs_force(struct pblk *pblk, int force)
a4bd217b4   Javier González   lightnvm: physica...
516
517
  {
  	struct pblk_gc *gc = &pblk->gc;
b20ba1bc7   Javier González   lightnvm: pblk: r...
518
519
520
  
  	if (force < 0 || force > 1)
  		return -EINVAL;
a4bd217b4   Javier González   lightnvm: physica...
521
522
  
  	spin_lock(&gc->lock);
a4bd217b4   Javier González   lightnvm: physica...
523
  	gc->gc_forced = force;
b20ba1bc7   Javier González   lightnvm: pblk: r...
524
525
526
527
528
  
  	if (force)
  		gc->gc_enabled = 1;
  	else
  		gc->gc_enabled = 0;
a4bd217b4   Javier González   lightnvm: physica...
529
  	spin_unlock(&gc->lock);
b20ba1bc7   Javier González   lightnvm: pblk: r...
530
531
532
533
  
  	pblk_gc_should_start(pblk);
  
  	return 0;
a4bd217b4   Javier González   lightnvm: physica...
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
  }
  
  int pblk_gc_init(struct pblk *pblk)
  {
  	struct pblk_gc *gc = &pblk->gc;
  	int ret;
  
  	gc->gc_ts = kthread_create(pblk_gc_ts, pblk, "pblk-gc-ts");
  	if (IS_ERR(gc->gc_ts)) {
  		pr_err("pblk: could not allocate GC main kthread
  ");
  		return PTR_ERR(gc->gc_ts);
  	}
  
  	gc->gc_writer_ts = kthread_create(pblk_gc_writer_ts, pblk,
  							"pblk-gc-writer-ts");
  	if (IS_ERR(gc->gc_writer_ts)) {
  		pr_err("pblk: could not allocate GC writer kthread
  ");
  		ret = PTR_ERR(gc->gc_writer_ts);
  		goto fail_free_main_kthread;
  	}
b20ba1bc7   Javier González   lightnvm: pblk: r...
556
557
558
559
560
561
562
563
  	gc->gc_reader_ts = kthread_create(pblk_gc_reader_ts, pblk,
  							"pblk-gc-reader-ts");
  	if (IS_ERR(gc->gc_reader_ts)) {
  		pr_err("pblk: could not allocate GC reader kthread
  ");
  		ret = PTR_ERR(gc->gc_reader_ts);
  		goto fail_free_writer_kthread;
  	}
a4bd217b4   Javier González   lightnvm: physica...
564
565
566
567
568
569
  	setup_timer(&gc->gc_timer, pblk_gc_timer, (unsigned long)pblk);
  	mod_timer(&gc->gc_timer, jiffies + msecs_to_jiffies(GC_TIME_MSECS));
  
  	gc->gc_active = 0;
  	gc->gc_forced = 0;
  	gc->gc_enabled = 1;
a4bd217b4   Javier González   lightnvm: physica...
570
571
  	gc->w_entries = 0;
  	atomic_set(&gc->inflight_gc, 0);
b20ba1bc7   Javier González   lightnvm: pblk: r...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
  	/* Workqueue that reads valid sectors from a line and submit them to the
  	 * GC writer to be recycled.
  	 */
  	gc->gc_line_reader_wq = alloc_workqueue("pblk-gc-line-reader-wq",
  			WQ_MEM_RECLAIM | WQ_UNBOUND, PBLK_GC_MAX_READERS);
  	if (!gc->gc_line_reader_wq) {
  		pr_err("pblk: could not allocate GC line reader workqueue
  ");
  		ret = -ENOMEM;
  		goto fail_free_reader_kthread;
  	}
  
  	/* Workqueue that prepare lines for GC */
  	gc->gc_reader_wq = alloc_workqueue("pblk-gc-line_wq",
  					WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
a4bd217b4   Javier González   lightnvm: physica...
587
588
589
590
  	if (!gc->gc_reader_wq) {
  		pr_err("pblk: could not allocate GC reader workqueue
  ");
  		ret = -ENOMEM;
b20ba1bc7   Javier González   lightnvm: pblk: r...
591
  		goto fail_free_reader_line_wq;
a4bd217b4   Javier González   lightnvm: physica...
592
593
594
595
  	}
  
  	spin_lock_init(&gc->lock);
  	spin_lock_init(&gc->w_lock);
b20ba1bc7   Javier González   lightnvm: pblk: r...
596
597
598
  	spin_lock_init(&gc->r_lock);
  
  	sema_init(&gc->gc_sem, 128);
a4bd217b4   Javier González   lightnvm: physica...
599
  	INIT_LIST_HEAD(&gc->w_list);
b20ba1bc7   Javier González   lightnvm: pblk: r...
600
  	INIT_LIST_HEAD(&gc->r_list);
a4bd217b4   Javier González   lightnvm: physica...
601
602
  
  	return 0;
b20ba1bc7   Javier González   lightnvm: pblk: r...
603
604
605
606
  fail_free_reader_line_wq:
  	destroy_workqueue(gc->gc_line_reader_wq);
  fail_free_reader_kthread:
  	kthread_stop(gc->gc_reader_ts);
a4bd217b4   Javier González   lightnvm: physica...
607
608
  fail_free_writer_kthread:
  	kthread_stop(gc->gc_writer_ts);
503ec94ec   Dan Carpenter   lightnvm: pblk-gc...
609
610
  fail_free_main_kthread:
  	kthread_stop(gc->gc_ts);
a4bd217b4   Javier González   lightnvm: physica...
611
612
613
614
615
616
617
618
619
  
  	return ret;
  }
  
  void pblk_gc_exit(struct pblk *pblk)
  {
  	struct pblk_gc *gc = &pblk->gc;
  
  	flush_workqueue(gc->gc_reader_wq);
b20ba1bc7   Javier González   lightnvm: pblk: r...
620
  	flush_workqueue(gc->gc_line_reader_wq);
a4bd217b4   Javier González   lightnvm: physica...
621

1b07f7511   Hans Holmberg   lightnvm: pblk: p...
622
623
  	gc->gc_enabled = 0;
  	del_timer_sync(&gc->gc_timer);
a4bd217b4   Javier González   lightnvm: physica...
624
625
626
627
  	pblk_gc_stop(pblk, 1);
  
  	if (gc->gc_ts)
  		kthread_stop(gc->gc_ts);
b20ba1bc7   Javier González   lightnvm: pblk: r...
628
629
630
631
632
  	if (gc->gc_reader_wq)
  		destroy_workqueue(gc->gc_reader_wq);
  
  	if (gc->gc_line_reader_wq)
  		destroy_workqueue(gc->gc_line_reader_wq);
a4bd217b4   Javier González   lightnvm: physica...
633
634
635
  
  	if (gc->gc_writer_ts)
  		kthread_stop(gc->gc_writer_ts);
b20ba1bc7   Javier González   lightnvm: pblk: r...
636
637
638
  
  	if (gc->gc_reader_ts)
  		kthread_stop(gc->gc_reader_ts);
a4bd217b4   Javier González   lightnvm: physica...
639
  }