Blame view

drivers/lightnvm/gennvm.c 14.8 KB
48add0f5a   Matias Bjørling   gennvm: Generic N...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /*
   * Copyright (C) 2015 Matias Bjorling <m@bjorling.me>
   *
   * 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.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; see the file COPYING.  If not, write to
   * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
   * USA.
   *
5e60edb7d   Matias Bjørling   lightnvm: rename ...
18
   * Implementation of a general nvm manager for Open-Channel SSDs.
48add0f5a   Matias Bjørling   gennvm: Generic N...
19
20
21
   */
  
  #include "gennvm.h"
b76eb20bb   Matias Bjørling   lightnvm: move ta...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  static struct nvm_target *gen_find_target(struct gen_dev *gn, const char *name)
  {
  	struct nvm_target *tgt;
  
  	list_for_each_entry(tgt, &gn->targets, list)
  		if (!strcmp(name, tgt->disk->disk_name))
  			return tgt;
  
  	return NULL;
  }
  
  static const struct block_device_operations gen_fops = {
  	.owner		= THIS_MODULE,
  };
  
  static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
  {
  	struct gen_dev *gn = dev->mp;
  	struct nvm_ioctl_create_simple *s = &create->conf.s;
  	struct request_queue *tqueue;
  	struct gendisk *tdisk;
  	struct nvm_tgt_type *tt;
  	struct nvm_target *t;
  	void *targetdata;
  
  	tt = nvm_find_target_type(create->tgttype, 1);
  	if (!tt) {
  		pr_err("nvm: target type %s not found
  ", create->tgttype);
  		return -EINVAL;
  	}
  
  	mutex_lock(&gn->lock);
  	t = gen_find_target(gn, create->tgtname);
  	if (t) {
  		pr_err("nvm: target name already exists.
  ");
  		mutex_unlock(&gn->lock);
  		return -EINVAL;
  	}
  	mutex_unlock(&gn->lock);
  
  	t = kmalloc(sizeof(struct nvm_target), GFP_KERNEL);
  	if (!t)
  		return -ENOMEM;
  
  	tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
  	if (!tqueue)
  		goto err_t;
  	blk_queue_make_request(tqueue, tt->make_rq);
  
  	tdisk = alloc_disk(0);
  	if (!tdisk)
  		goto err_queue;
  
  	sprintf(tdisk->disk_name, "%s", create->tgtname);
  	tdisk->flags = GENHD_FL_EXT_DEVT;
  	tdisk->major = 0;
  	tdisk->first_minor = 0;
  	tdisk->fops = &gen_fops;
  	tdisk->queue = tqueue;
  
  	targetdata = tt->init(dev, tdisk, s->lun_begin, s->lun_end);
  	if (IS_ERR(targetdata))
  		goto err_init;
  
  	tdisk->private_data = targetdata;
  	tqueue->queuedata = targetdata;
  
  	blk_queue_max_hw_sectors(tqueue, 8 * dev->ops->max_phys_sect);
  
  	set_capacity(tdisk, tt->capacity(targetdata));
  	add_disk(tdisk);
  
  	t->type = tt;
  	t->disk = tdisk;
  	t->dev = dev;
  
  	mutex_lock(&gn->lock);
  	list_add_tail(&t->list, &gn->targets);
  	mutex_unlock(&gn->lock);
  
  	return 0;
  err_init:
  	put_disk(tdisk);
  err_queue:
  	blk_cleanup_queue(tqueue);
  err_t:
  	kfree(t);
  	return -ENOMEM;
  }
  
  static void __gen_remove_target(struct nvm_target *t)
  {
  	struct nvm_tgt_type *tt = t->type;
  	struct gendisk *tdisk = t->disk;
  	struct request_queue *q = tdisk->queue;
  
  	del_gendisk(tdisk);
  	blk_cleanup_queue(q);
  
  	if (tt->exit)
  		tt->exit(tdisk->private_data);
  
  	put_disk(tdisk);
  
  	list_del(&t->list);
  	kfree(t);
  }
  
  /**
   * gen_remove_tgt - Removes a target from the media manager
   * @dev:	device
   * @remove:	ioctl structure with target name to remove.
   *
   * Returns:
   * 0: on success
   * 1: on not found
   * <0: on error
   */
  static int gen_remove_tgt(struct nvm_dev *dev, struct nvm_ioctl_remove *remove)
  {
  	struct gen_dev *gn = dev->mp;
  	struct nvm_target *t;
  
  	if (!gn)
  		return 1;
  
  	mutex_lock(&gn->lock);
  	t = gen_find_target(gn, remove->tgtname);
  	if (!t) {
  		mutex_unlock(&gn->lock);
  		return 1;
  	}
  	__gen_remove_target(t);
  	mutex_unlock(&gn->lock);
  
  	return 0;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
161
  static int gen_get_area(struct nvm_dev *dev, sector_t *lba, sector_t len)
4c9dacb82   Wenwei Tao   lightnvm: specify...
162
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
163
164
  	struct gen_dev *gn = dev->mp;
  	struct gen_area *area, *prev, *next;
4c9dacb82   Wenwei Tao   lightnvm: specify...
165
166
167
168
169
  	sector_t begin = 0;
  	sector_t max_sectors = (dev->sec_size * dev->total_secs) >> 9;
  
  	if (len > max_sectors)
  		return -EINVAL;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
170
  	area = kmalloc(sizeof(struct gen_area), GFP_KERNEL);
4c9dacb82   Wenwei Tao   lightnvm: specify...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  	if (!area)
  		return -ENOMEM;
  
  	prev = NULL;
  
  	spin_lock(&dev->lock);
  	list_for_each_entry(next, &gn->area_list, list) {
  		if (begin + len > next->begin) {
  			begin = next->end;
  			prev = next;
  			continue;
  		}
  		break;
  	}
  
  	if ((begin + len) > max_sectors) {
  		spin_unlock(&dev->lock);
  		kfree(area);
  		return -EINVAL;
  	}
  
  	area->begin = *lba = begin;
  	area->end = begin + len;
  
  	if (prev) /* insert into sorted order */
  		list_add(&area->list, &prev->list);
  	else
  		list_add(&area->list, &gn->area_list);
  	spin_unlock(&dev->lock);
  
  	return 0;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
203
  static void gen_put_area(struct nvm_dev *dev, sector_t begin)
4c9dacb82   Wenwei Tao   lightnvm: specify...
204
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
205
206
  	struct gen_dev *gn = dev->mp;
  	struct gen_area *area;
4c9dacb82   Wenwei Tao   lightnvm: specify...
207
208
209
210
211
212
213
214
215
216
217
218
219
  
  	spin_lock(&dev->lock);
  	list_for_each_entry(area, &gn->area_list, list) {
  		if (area->begin != begin)
  			continue;
  
  		list_del(&area->list);
  		spin_unlock(&dev->lock);
  		kfree(area);
  		return;
  	}
  	spin_unlock(&dev->lock);
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
220
  static void gen_blocks_free(struct nvm_dev *dev)
48add0f5a   Matias Bjørling   gennvm: Generic N...
221
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
222
  	struct gen_dev *gn = dev->mp;
48add0f5a   Matias Bjørling   gennvm: Generic N...
223
224
  	struct gen_lun *lun;
  	int i;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
225
  	gen_for_each_lun(gn, lun, i) {
48add0f5a   Matias Bjørling   gennvm: Generic N...
226
227
228
229
230
  		if (!lun->vlun.blocks)
  			break;
  		vfree(lun->vlun.blocks);
  	}
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
231
  static void gen_luns_free(struct nvm_dev *dev)
48add0f5a   Matias Bjørling   gennvm: Generic N...
232
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
233
  	struct gen_dev *gn = dev->mp;
48add0f5a   Matias Bjørling   gennvm: Generic N...
234
235
236
  
  	kfree(gn->luns);
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
237
  static int gen_luns_init(struct nvm_dev *dev, struct gen_dev *gn)
48add0f5a   Matias Bjørling   gennvm: Generic N...
238
239
240
241
242
243
244
  {
  	struct gen_lun *lun;
  	int i;
  
  	gn->luns = kcalloc(dev->nr_luns, sizeof(struct gen_lun), GFP_KERNEL);
  	if (!gn->luns)
  		return -ENOMEM;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
245
  	gen_for_each_lun(gn, lun, i) {
48add0f5a   Matias Bjørling   gennvm: Generic N...
246
247
248
249
250
251
252
253
254
255
256
257
258
  		spin_lock_init(&lun->vlun.lock);
  		INIT_LIST_HEAD(&lun->free_list);
  		INIT_LIST_HEAD(&lun->used_list);
  		INIT_LIST_HEAD(&lun->bb_list);
  
  		lun->reserved_blocks = 2; /* for GC only */
  		lun->vlun.id = i;
  		lun->vlun.lun_id = i % dev->luns_per_chnl;
  		lun->vlun.chnl_id = i / dev->luns_per_chnl;
  		lun->vlun.nr_free_blocks = dev->blks_per_lun;
  	}
  	return 0;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
259
  static int gen_block_bb(struct gen_dev *gn, struct ppa_addr ppa,
e11903f5d   Matias Bjørling   lightnvm: refacto...
260
  							u8 *blks, int nr_blks)
48add0f5a   Matias Bjørling   gennvm: Generic N...
261
  {
e11903f5d   Matias Bjørling   lightnvm: refacto...
262
  	struct nvm_dev *dev = gn->dev;
114504698   Matias Bjørling   lightnvm: update ...
263
  	struct gen_lun *lun;
48add0f5a   Matias Bjørling   gennvm: Generic N...
264
265
  	struct nvm_block *blk;
  	int i;
22e8c9766   Matias Bjørling   lightnvm: move bl...
266
267
268
  	nr_blks = nvm_bb_tbl_fold(dev, blks, nr_blks);
  	if (nr_blks < 0)
  		return nr_blks;
c3293a9ac   Matias Bjørling   lightnvm: wrong o...
269
  	lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
114504698   Matias Bjørling   lightnvm: update ...
270

22e8c9766   Matias Bjørling   lightnvm: move bl...
271
  	for (i = 0; i < nr_blks; i++) {
114504698   Matias Bjørling   lightnvm: update ...
272
273
  		if (blks[i] == 0)
  			continue;
48add0f5a   Matias Bjørling   gennvm: Generic N...
274

48add0f5a   Matias Bjørling   gennvm: Generic N...
275
  		blk = &lun->vlun.blocks[i];
48add0f5a   Matias Bjørling   gennvm: Generic N...
276
  		list_move_tail(&blk->list, &lun->bb_list);
bdded1552   Chao Yu   lightnvm: fix inc...
277
  		lun->vlun.nr_free_blocks--;
48add0f5a   Matias Bjørling   gennvm: Generic N...
278
279
280
281
  	}
  
  	return 0;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
282
  static int gen_block_map(u64 slba, u32 nlb, __le64 *entries, void *private)
48add0f5a   Matias Bjørling   gennvm: Generic N...
283
284
  {
  	struct nvm_dev *dev = private;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
285
  	struct gen_dev *gn = dev->mp;
48add0f5a   Matias Bjørling   gennvm: Generic N...
286
287
288
289
290
  	u64 elba = slba + nlb;
  	struct gen_lun *lun;
  	struct nvm_block *blk;
  	u64 i;
  	int lun_id;
4ece44af7   Matias Bjørling   lightnvm: rename ...
291
  	if (unlikely(elba > dev->total_secs)) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
292
293
  		pr_err("gen: L2P data from device is out of bounds!
  ");
48add0f5a   Matias Bjørling   gennvm: Generic N...
294
295
296
297
298
  		return -EINVAL;
  	}
  
  	for (i = 0; i < nlb; i++) {
  		u64 pba = le64_to_cpu(entries[i]);
4ece44af7   Matias Bjørling   lightnvm: rename ...
299
  		if (unlikely(pba >= dev->total_secs && pba != U64_MAX)) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
300
301
  			pr_err("gen: L2P data entry is out of bounds!
  ");
48add0f5a   Matias Bjørling   gennvm: Generic N...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  			return -EINVAL;
  		}
  
  		/* Address zero is a special one. The first page on a disk is
  		 * protected. It often holds internal device boot
  		 * information.
  		 */
  		if (!pba)
  			continue;
  
  		/* resolve block from physical address */
  		lun_id = div_u64(pba, dev->sec_per_lun);
  		lun = &gn->luns[lun_id];
  
  		/* Calculate block offset into lun */
  		pba = pba - (dev->sec_per_lun * lun_id);
  		blk = &lun->vlun.blocks[div_u64(pba, dev->sec_per_blk)];
ff0e498bf   Javier González   lightnvm: manage ...
319
  		if (!blk->state) {
48add0f5a   Matias Bjørling   gennvm: Generic N...
320
321
  			/* at this point, we don't know anything about the
  			 * block. It's up to the FTL on top to re-etablish the
ff0e498bf   Javier González   lightnvm: manage ...
322
  			 * block state. The block is assumed to be open.
48add0f5a   Matias Bjørling   gennvm: Generic N...
323
324
  			 */
  			list_move_tail(&blk->list, &lun->used_list);
077d23899   Matias Bjørling   lightnvm: remove ...
325
  			blk->state = NVM_BLK_ST_TGT;
48add0f5a   Matias Bjørling   gennvm: Generic N...
326
327
328
329
330
331
  			lun->vlun.nr_free_blocks--;
  		}
  	}
  
  	return 0;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
332
  static int gen_blocks_init(struct nvm_dev *dev, struct gen_dev *gn)
48add0f5a   Matias Bjørling   gennvm: Generic N...
333
334
335
336
  {
  	struct gen_lun *lun;
  	struct nvm_block *block;
  	sector_t lun_iter, blk_iter, cur_block_id = 0;
e11903f5d   Matias Bjørling   lightnvm: refacto...
337
338
339
340
341
342
343
  	int ret, nr_blks;
  	u8 *blks;
  
  	nr_blks = dev->blks_per_lun * dev->plane_mode;
  	blks = kmalloc(nr_blks, GFP_KERNEL);
  	if (!blks)
  		return -ENOMEM;
48add0f5a   Matias Bjørling   gennvm: Generic N...
344

5e60edb7d   Matias Bjørling   lightnvm: rename ...
345
  	gen_for_each_lun(gn, lun, lun_iter) {
48add0f5a   Matias Bjørling   gennvm: Generic N...
346
347
  		lun->vlun.blocks = vzalloc(sizeof(struct nvm_block) *
  							dev->blks_per_lun);
e11903f5d   Matias Bjørling   lightnvm: refacto...
348
349
  		if (!lun->vlun.blocks) {
  			kfree(blks);
48add0f5a   Matias Bjørling   gennvm: Generic N...
350
  			return -ENOMEM;
e11903f5d   Matias Bjørling   lightnvm: refacto...
351
  		}
48add0f5a   Matias Bjørling   gennvm: Generic N...
352
353
354
355
356
357
358
359
360
361
  
  		for (blk_iter = 0; blk_iter < dev->blks_per_lun; blk_iter++) {
  			block = &lun->vlun.blocks[blk_iter];
  
  			INIT_LIST_HEAD(&block->list);
  
  			block->lun = &lun->vlun;
  			block->id = cur_block_id++;
  
  			/* First block is reserved for device */
0b59733b9   Javier Gonzalez   lightnvm: keep tr...
362
363
  			if (unlikely(lun_iter == 0 && blk_iter == 0)) {
  				lun->vlun.nr_free_blocks--;
48add0f5a   Matias Bjørling   gennvm: Generic N...
364
  				continue;
0b59733b9   Javier Gonzalez   lightnvm: keep tr...
365
  			}
48add0f5a   Matias Bjørling   gennvm: Generic N...
366
367
368
369
370
  
  			list_add_tail(&block->list, &lun->free_list);
  		}
  
  		if (dev->ops->get_bb_tbl) {
114504698   Matias Bjørling   lightnvm: update ...
371
372
373
374
  			struct ppa_addr ppa;
  
  			ppa.ppa = 0;
  			ppa.g.ch = lun->vlun.chnl_id;
293a6e8e2   Matias Bjørling   lightnvm: fix out...
375
  			ppa.g.lun = lun->vlun.lun_id;
114504698   Matias Bjørling   lightnvm: update ...
376

e11903f5d   Matias Bjørling   lightnvm: refacto...
377
378
  			ret = nvm_get_bb_tbl(dev, ppa, blks);
  			if (ret)
5e60edb7d   Matias Bjørling   lightnvm: rename ...
379
380
  				pr_err("gen: could not get BB table
  ");
e11903f5d   Matias Bjørling   lightnvm: refacto...
381

5e60edb7d   Matias Bjørling   lightnvm: rename ...
382
  			ret = gen_block_bb(gn, ppa, blks, nr_blks);
48add0f5a   Matias Bjørling   gennvm: Generic N...
383
  			if (ret)
5e60edb7d   Matias Bjørling   lightnvm: rename ...
384
385
  				pr_err("gen: BB table map failed
  ");
48add0f5a   Matias Bjørling   gennvm: Generic N...
386
387
  		}
  	}
29fd20b8e   Javier González   lightnvm: do not ...
388
  	if ((dev->identity.dom & NVM_RSP_L2P) && dev->ops->get_l2p_tbl) {
4ece44af7   Matias Bjørling   lightnvm: rename ...
389
  		ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_secs,
5e60edb7d   Matias Bjørling   lightnvm: rename ...
390
  							gen_block_map, dev);
48add0f5a   Matias Bjørling   gennvm: Generic N...
391
  		if (ret) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
392
393
394
  			pr_err("gen: could not read L2P table.
  ");
  			pr_warn("gen: default block initialization");
48add0f5a   Matias Bjørling   gennvm: Generic N...
395
396
  		}
  	}
e11903f5d   Matias Bjørling   lightnvm: refacto...
397
  	kfree(blks);
48add0f5a   Matias Bjørling   gennvm: Generic N...
398
399
  	return 0;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
400
  static void gen_free(struct nvm_dev *dev)
8261bd48c   Wenwei Tao   lightnvm: free me...
401
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
402
403
  	gen_blocks_free(dev);
  	gen_luns_free(dev);
8261bd48c   Wenwei Tao   lightnvm: free me...
404
405
406
  	kfree(dev->mp);
  	dev->mp = NULL;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
407
  static int gen_register(struct nvm_dev *dev)
48add0f5a   Matias Bjørling   gennvm: Generic N...
408
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
409
  	struct gen_dev *gn;
48add0f5a   Matias Bjørling   gennvm: Generic N...
410
  	int ret;
008b74438   Matias Bjørling   lightnvm: prevent...
411
412
  	if (!try_module_get(THIS_MODULE))
  		return -ENODEV;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
413
  	gn = kzalloc(sizeof(struct gen_dev), GFP_KERNEL);
48add0f5a   Matias Bjørling   gennvm: Generic N...
414
415
  	if (!gn)
  		return -ENOMEM;
114504698   Matias Bjørling   lightnvm: update ...
416
  	gn->dev = dev;
48add0f5a   Matias Bjørling   gennvm: Generic N...
417
  	gn->nr_luns = dev->nr_luns;
4c9dacb82   Wenwei Tao   lightnvm: specify...
418
  	INIT_LIST_HEAD(&gn->area_list);
b76eb20bb   Matias Bjørling   lightnvm: move ta...
419
420
  	mutex_init(&gn->lock);
  	INIT_LIST_HEAD(&gn->targets);
48add0f5a   Matias Bjørling   gennvm: Generic N...
421
  	dev->mp = gn;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
422
  	ret = gen_luns_init(dev, gn);
48add0f5a   Matias Bjørling   gennvm: Generic N...
423
  	if (ret) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
424
425
  		pr_err("gen: could not initialize luns
  ");
48add0f5a   Matias Bjørling   gennvm: Generic N...
426
427
  		goto err;
  	}
5e60edb7d   Matias Bjørling   lightnvm: rename ...
428
  	ret = gen_blocks_init(dev, gn);
48add0f5a   Matias Bjørling   gennvm: Generic N...
429
  	if (ret) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
430
431
  		pr_err("gen: could not initialize blocks
  ");
48add0f5a   Matias Bjørling   gennvm: Generic N...
432
433
434
435
436
  		goto err;
  	}
  
  	return 1;
  err:
5e60edb7d   Matias Bjørling   lightnvm: rename ...
437
  	gen_free(dev);
008b74438   Matias Bjørling   lightnvm: prevent...
438
  	module_put(THIS_MODULE);
48add0f5a   Matias Bjørling   gennvm: Generic N...
439
440
  	return ret;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
441
  static void gen_unregister(struct nvm_dev *dev)
48add0f5a   Matias Bjørling   gennvm: Generic N...
442
  {
b76eb20bb   Matias Bjørling   lightnvm: move ta...
443
444
445
446
447
448
449
450
451
452
  	struct gen_dev *gn = dev->mp;
  	struct nvm_target *t, *tmp;
  
  	mutex_lock(&gn->lock);
  	list_for_each_entry_safe(t, tmp, &gn->targets, list) {
  		if (t->dev != dev)
  			continue;
  		__gen_remove_target(t);
  	}
  	mutex_unlock(&gn->lock);
5e60edb7d   Matias Bjørling   lightnvm: rename ...
453
  	gen_free(dev);
008b74438   Matias Bjørling   lightnvm: prevent...
454
  	module_put(THIS_MODULE);
48add0f5a   Matias Bjørling   gennvm: Generic N...
455
  }
41285fad5   Matias Bjørling   lightnvm: remove ...
456
  static struct nvm_block *gen_get_blk(struct nvm_dev *dev,
48add0f5a   Matias Bjørling   gennvm: Generic N...
457
458
459
460
461
  				struct nvm_lun *vlun, unsigned long flags)
  {
  	struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun);
  	struct nvm_block *blk = NULL;
  	int is_gc = flags & NVM_IOTYPE_GC;
41285fad5   Matias Bjørling   lightnvm: remove ...
462
  	spin_lock(&vlun->lock);
48add0f5a   Matias Bjørling   gennvm: Generic N...
463
  	if (list_empty(&lun->free_list)) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
464
  		pr_err_ratelimited("gen: lun %u have no free pages available",
48add0f5a   Matias Bjørling   gennvm: Generic N...
465
  								lun->vlun.id);
48add0f5a   Matias Bjørling   gennvm: Generic N...
466
467
  		goto out;
  	}
e9b76a80f   Wenwei Tao   lightnvm: refacto...
468
  	if (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks)
48add0f5a   Matias Bjørling   gennvm: Generic N...
469
  		goto out;
48add0f5a   Matias Bjørling   gennvm: Generic N...
470
471
  
  	blk = list_first_entry(&lun->free_list, struct nvm_block, list);
48add0f5a   Matias Bjørling   gennvm: Generic N...
472

077d23899   Matias Bjørling   lightnvm: remove ...
473
474
  	list_move_tail(&blk->list, &lun->used_list);
  	blk->state = NVM_BLK_ST_TGT;
48add0f5a   Matias Bjørling   gennvm: Generic N...
475
  	lun->vlun.nr_free_blocks--;
48add0f5a   Matias Bjørling   gennvm: Generic N...
476
  out:
e9b76a80f   Wenwei Tao   lightnvm: refacto...
477
  	spin_unlock(&vlun->lock);
48add0f5a   Matias Bjørling   gennvm: Generic N...
478
479
  	return blk;
  }
41285fad5   Matias Bjørling   lightnvm: remove ...
480
  static void gen_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
48add0f5a   Matias Bjørling   gennvm: Generic N...
481
482
483
  {
  	struct nvm_lun *vlun = blk->lun;
  	struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun);
41285fad5   Matias Bjørling   lightnvm: remove ...
484
  	spin_lock(&vlun->lock);
077d23899   Matias Bjørling   lightnvm: remove ...
485
  	if (blk->state & NVM_BLK_ST_TGT) {
ff0e498bf   Javier González   lightnvm: manage ...
486
  		list_move_tail(&blk->list, &lun->free_list);
ff0e498bf   Javier González   lightnvm: manage ...
487
488
489
  		lun->vlun.nr_free_blocks++;
  		blk->state = NVM_BLK_ST_FREE;
  	} else if (blk->state & NVM_BLK_ST_BAD) {
48add0f5a   Matias Bjørling   gennvm: Generic N...
490
  		list_move_tail(&blk->list, &lun->bb_list);
ff0e498bf   Javier González   lightnvm: manage ...
491
492
  		blk->state = NVM_BLK_ST_BAD;
  	} else {
48add0f5a   Matias Bjørling   gennvm: Generic N...
493
  		WARN_ON_ONCE(1);
5e60edb7d   Matias Bjørling   lightnvm: rename ...
494
495
  		pr_err("gen: erroneous block type (%lu -> %u)
  ",
ff0e498bf   Javier González   lightnvm: manage ...
496
  							blk->id, blk->state);
48add0f5a   Matias Bjørling   gennvm: Generic N...
497
498
  		list_move_tail(&blk->list, &lun->bb_list);
  	}
48add0f5a   Matias Bjørling   gennvm: Generic N...
499
500
  	spin_unlock(&vlun->lock);
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
501
  static void gen_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type)
48add0f5a   Matias Bjørling   gennvm: Generic N...
502
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
503
  	struct gen_dev *gn = dev->mp;
48add0f5a   Matias Bjørling   gennvm: Generic N...
504
505
  	struct gen_lun *lun;
  	struct nvm_block *blk;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
506
507
  	pr_debug("gen: ppa  (ch: %u lun: %u blk: %u pg: %u) -> %u
  ",
04a8aa173   Matias Bjørling   lightnvm: expose ...
508
  			ppa.g.ch, ppa.g.lun, ppa.g.blk, ppa.g.pg, type);
a63d5cf20   Matias Bjørling   lightnvm: move re...
509

04a8aa173   Matias Bjørling   lightnvm: expose ...
510
511
512
  	if (unlikely(ppa.g.ch > dev->nr_chnls ||
  					ppa.g.lun > dev->luns_per_chnl ||
  					ppa.g.blk > dev->blks_per_lun)) {
48add0f5a   Matias Bjørling   gennvm: Generic N...
513
  		WARN_ON_ONCE(1);
5e60edb7d   Matias Bjørling   lightnvm: rename ...
514
  		pr_err("gen: ppa broken (ch: %u > %u lun: %u > %u blk: %u > %u",
04a8aa173   Matias Bjørling   lightnvm: expose ...
515
516
517
  				ppa.g.ch, dev->nr_chnls,
  				ppa.g.lun, dev->luns_per_chnl,
  				ppa.g.blk, dev->blks_per_lun);
48add0f5a   Matias Bjørling   gennvm: Generic N...
518
519
  		return;
  	}
24d4a7d72   Matias Bjørling   lightnvm: fix lun...
520
  	lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
04a8aa173   Matias Bjørling   lightnvm: expose ...
521
  	blk = &lun->vlun.blocks[ppa.g.blk];
48add0f5a   Matias Bjørling   gennvm: Generic N...
522
523
  
  	/* will be moved to bb list on put_blk from target */
ff0e498bf   Javier González   lightnvm: manage ...
524
  	blk->state = type;
48add0f5a   Matias Bjørling   gennvm: Generic N...
525
  }
a63d5cf20   Matias Bjørling   lightnvm: move re...
526
  /*
5e60edb7d   Matias Bjørling   lightnvm: rename ...
527
   * mark block bad in gen. It is expected that the target recovers separately
a63d5cf20   Matias Bjørling   lightnvm: move re...
528
   */
5e60edb7d   Matias Bjørling   lightnvm: rename ...
529
  static void gen_mark_blk_bad(struct nvm_dev *dev, struct nvm_rq *rqd)
48add0f5a   Matias Bjørling   gennvm: Generic N...
530
  {
a63d5cf20   Matias Bjørling   lightnvm: move re...
531
532
533
  	int bit = -1;
  	int max_secs = dev->ops->max_phys_sect;
  	void *comp_bits = &rqd->ppa_status;
48add0f5a   Matias Bjørling   gennvm: Generic N...
534

069368e91   Matias Bjørling   lightnvm: move pp...
535
  	nvm_addr_to_generic_mode(dev, rqd);
48add0f5a   Matias Bjørling   gennvm: Generic N...
536
537
  
  	/* look up blocks and mark them as bad */
6d5be9590   Javier González   lightnvm: rename ...
538
  	if (rqd->nr_ppas == 1) {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
539
  		gen_mark_blk(dev, rqd->ppa_addr, NVM_BLK_ST_BAD);
a63d5cf20   Matias Bjørling   lightnvm: move re...
540
541
542
543
  		return;
  	}
  
  	while ((bit = find_next_bit(comp_bits, max_secs, bit + 1)) < max_secs)
5e60edb7d   Matias Bjørling   lightnvm: rename ...
544
  		gen_mark_blk(dev, rqd->ppa_list[bit], NVM_BLK_ST_BAD);
48add0f5a   Matias Bjørling   gennvm: Generic N...
545
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
546
  static void gen_end_io(struct nvm_rq *rqd)
48add0f5a   Matias Bjørling   gennvm: Generic N...
547
548
  {
  	struct nvm_tgt_instance *ins = rqd->ins;
48add0f5a   Matias Bjørling   gennvm: Generic N...
549

a63d5cf20   Matias Bjørling   lightnvm: move re...
550
  	if (rqd->error == NVM_RSP_ERR_FAILWRITE)
5e60edb7d   Matias Bjørling   lightnvm: rename ...
551
  		gen_mark_blk_bad(rqd->dev, rqd);
48add0f5a   Matias Bjørling   gennvm: Generic N...
552

72d256ecc   Matias Bjørling   lightnvm: move rq...
553
  	ins->tt->end_io(rqd);
91276162d   Matias Bjørling   lightnvm: refacto...
554
  }
48add0f5a   Matias Bjørling   gennvm: Generic N...
555

5e60edb7d   Matias Bjørling   lightnvm: rename ...
556
  static int gen_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd)
91276162d   Matias Bjørling   lightnvm: refacto...
557
558
559
560
561
562
563
564
  {
  	if (!dev->ops->submit_io)
  		return -ENODEV;
  
  	/* Convert address space */
  	nvm_generic_to_addr_mode(dev, rqd);
  
  	rqd->dev = dev;
5e60edb7d   Matias Bjørling   lightnvm: rename ...
565
  	rqd->end_io = gen_end_io;
91276162d   Matias Bjørling   lightnvm: refacto...
566
  	return dev->ops->submit_io(dev, rqd);
48add0f5a   Matias Bjørling   gennvm: Generic N...
567
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
568
  static int gen_erase_blk(struct nvm_dev *dev, struct nvm_block *blk,
48add0f5a   Matias Bjørling   gennvm: Generic N...
569
570
  							unsigned long flags)
  {
069368e91   Matias Bjørling   lightnvm: move pp...
571
  	struct ppa_addr addr = block_to_ppa(dev, blk);
48add0f5a   Matias Bjørling   gennvm: Generic N...
572

81e681d3f   Matias Bjørling   lightnvm: support...
573
  	return nvm_erase_ppa(dev, &addr, 1);
48add0f5a   Matias Bjørling   gennvm: Generic N...
574
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
575
  static int gen_reserve_lun(struct nvm_dev *dev, int lunid)
da1e28491   Wenwei Tao   lightnvm: add a b...
576
577
578
  {
  	return test_and_set_bit(lunid, dev->lun_map);
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
579
  static void gen_release_lun(struct nvm_dev *dev, int lunid)
da1e28491   Wenwei Tao   lightnvm: add a b...
580
581
582
  {
  	WARN_ON(!test_and_clear_bit(lunid, dev->lun_map));
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
583
  static struct nvm_lun *gen_get_lun(struct nvm_dev *dev, int lunid)
48add0f5a   Matias Bjørling   gennvm: Generic N...
584
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
585
  	struct gen_dev *gn = dev->mp;
48add0f5a   Matias Bjørling   gennvm: Generic N...
586

da1e28491   Wenwei Tao   lightnvm: add a b...
587
588
  	if (unlikely(lunid >= dev->nr_luns))
  		return NULL;
48add0f5a   Matias Bjørling   gennvm: Generic N...
589
590
  	return &gn->luns[lunid].vlun;
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
591
  static void gen_lun_info_print(struct nvm_dev *dev)
48add0f5a   Matias Bjørling   gennvm: Generic N...
592
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
593
  	struct gen_dev *gn = dev->mp;
48add0f5a   Matias Bjørling   gennvm: Generic N...
594
595
  	struct gen_lun *lun;
  	unsigned int i;
2fde0e482   Javier Gonzalez   lightnvm: add fre...
596

5e60edb7d   Matias Bjørling   lightnvm: rename ...
597
  	gen_for_each_lun(gn, lun, i) {
2fde0e482   Javier Gonzalez   lightnvm: add fre...
598
  		spin_lock(&lun->vlun.lock);
077d23899   Matias Bjørling   lightnvm: remove ...
599
600
601
  		pr_info("%s: lun%8u\t%u
  ", dev->name, i,
  						lun->vlun.nr_free_blocks);
2fde0e482   Javier Gonzalez   lightnvm: add fre...
602
603
604
  
  		spin_unlock(&lun->vlun.lock);
  	}
48add0f5a   Matias Bjørling   gennvm: Generic N...
605
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
606
  static struct nvmm_type gen = {
ff0e498bf   Javier González   lightnvm: manage ...
607
608
  	.name			= "gennvm",
  	.version		= {0, 1, 0},
5e60edb7d   Matias Bjørling   lightnvm: rename ...
609
610
  	.register_mgr		= gen_register,
  	.unregister_mgr		= gen_unregister,
48add0f5a   Matias Bjørling   gennvm: Generic N...
611

b76eb20bb   Matias Bjørling   lightnvm: move ta...
612
613
  	.create_tgt		= gen_create_tgt,
  	.remove_tgt		= gen_remove_tgt,
5e60edb7d   Matias Bjørling   lightnvm: rename ...
614
615
  	.get_blk		= gen_get_blk,
  	.put_blk		= gen_put_blk,
48add0f5a   Matias Bjørling   gennvm: Generic N...
616

5e60edb7d   Matias Bjørling   lightnvm: rename ...
617
618
  	.submit_io		= gen_submit_io,
  	.erase_blk		= gen_erase_blk,
48add0f5a   Matias Bjørling   gennvm: Generic N...
619

5e60edb7d   Matias Bjørling   lightnvm: rename ...
620
  	.mark_blk		= gen_mark_blk,
04a8aa173   Matias Bjørling   lightnvm: expose ...
621

5e60edb7d   Matias Bjørling   lightnvm: rename ...
622
623
624
625
  	.get_lun		= gen_get_lun,
  	.reserve_lun		= gen_reserve_lun,
  	.release_lun		= gen_release_lun,
  	.lun_info_print		= gen_lun_info_print,
4c9dacb82   Wenwei Tao   lightnvm: specify...
626

5e60edb7d   Matias Bjørling   lightnvm: rename ...
627
628
  	.get_area		= gen_get_area,
  	.put_area		= gen_put_area,
4c9dacb82   Wenwei Tao   lightnvm: specify...
629

48add0f5a   Matias Bjørling   gennvm: Generic N...
630
  };
5e60edb7d   Matias Bjørling   lightnvm: rename ...
631
  static int __init gen_module_init(void)
48add0f5a   Matias Bjørling   gennvm: Generic N...
632
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
633
  	return nvm_register_mgr(&gen);
48add0f5a   Matias Bjørling   gennvm: Generic N...
634
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
635
  static void gen_module_exit(void)
48add0f5a   Matias Bjørling   gennvm: Generic N...
636
  {
5e60edb7d   Matias Bjørling   lightnvm: rename ...
637
  	nvm_unregister_mgr(&gen);
48add0f5a   Matias Bjørling   gennvm: Generic N...
638
  }
5e60edb7d   Matias Bjørling   lightnvm: rename ...
639
640
  module_init(gen_module_init);
  module_exit(gen_module_exit);
48add0f5a   Matias Bjørling   gennvm: Generic N...
641
  MODULE_LICENSE("GPL v2");
5e60edb7d   Matias Bjørling   lightnvm: rename ...
642
  MODULE_DESCRIPTION("General media manager for Open-Channel SSDs");