Blame view
drivers/lightnvm/gennvm.c
14.8 KB
48add0f5a 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 lightnvm: rename ... |
18 |
* Implementation of a general nvm manager for Open-Channel SSDs. |
48add0f5a gennvm: Generic N... |
19 20 21 |
*/ #include "gennvm.h" |
b76eb20bb 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 lightnvm: rename ... |
161 |
static int gen_get_area(struct nvm_dev *dev, sector_t *lba, sector_t len) |
4c9dacb82 lightnvm: specify... |
162 |
{ |
5e60edb7d lightnvm: rename ... |
163 164 |
struct gen_dev *gn = dev->mp; struct gen_area *area, *prev, *next; |
4c9dacb82 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 lightnvm: rename ... |
170 |
area = kmalloc(sizeof(struct gen_area), GFP_KERNEL); |
4c9dacb82 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 lightnvm: rename ... |
203 |
static void gen_put_area(struct nvm_dev *dev, sector_t begin) |
4c9dacb82 lightnvm: specify... |
204 |
{ |
5e60edb7d lightnvm: rename ... |
205 206 |
struct gen_dev *gn = dev->mp; struct gen_area *area; |
4c9dacb82 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 lightnvm: rename ... |
220 |
static void gen_blocks_free(struct nvm_dev *dev) |
48add0f5a gennvm: Generic N... |
221 |
{ |
5e60edb7d lightnvm: rename ... |
222 |
struct gen_dev *gn = dev->mp; |
48add0f5a gennvm: Generic N... |
223 224 |
struct gen_lun *lun; int i; |
5e60edb7d lightnvm: rename ... |
225 |
gen_for_each_lun(gn, lun, i) { |
48add0f5a gennvm: Generic N... |
226 227 228 229 230 |
if (!lun->vlun.blocks) break; vfree(lun->vlun.blocks); } } |
5e60edb7d lightnvm: rename ... |
231 |
static void gen_luns_free(struct nvm_dev *dev) |
48add0f5a gennvm: Generic N... |
232 |
{ |
5e60edb7d lightnvm: rename ... |
233 |
struct gen_dev *gn = dev->mp; |
48add0f5a gennvm: Generic N... |
234 235 236 |
kfree(gn->luns); } |
5e60edb7d lightnvm: rename ... |
237 |
static int gen_luns_init(struct nvm_dev *dev, struct gen_dev *gn) |
48add0f5a 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 lightnvm: rename ... |
245 |
gen_for_each_lun(gn, lun, i) { |
48add0f5a 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 lightnvm: rename ... |
259 |
static int gen_block_bb(struct gen_dev *gn, struct ppa_addr ppa, |
e11903f5d lightnvm: refacto... |
260 |
u8 *blks, int nr_blks) |
48add0f5a gennvm: Generic N... |
261 |
{ |
e11903f5d lightnvm: refacto... |
262 |
struct nvm_dev *dev = gn->dev; |
114504698 lightnvm: update ... |
263 |
struct gen_lun *lun; |
48add0f5a gennvm: Generic N... |
264 265 |
struct nvm_block *blk; int i; |
22e8c9766 lightnvm: move bl... |
266 267 268 |
nr_blks = nvm_bb_tbl_fold(dev, blks, nr_blks); if (nr_blks < 0) return nr_blks; |
c3293a9ac lightnvm: wrong o... |
269 |
lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun]; |
114504698 lightnvm: update ... |
270 |
|
22e8c9766 lightnvm: move bl... |
271 |
for (i = 0; i < nr_blks; i++) { |
114504698 lightnvm: update ... |
272 273 |
if (blks[i] == 0) continue; |
48add0f5a gennvm: Generic N... |
274 |
|
48add0f5a gennvm: Generic N... |
275 |
blk = &lun->vlun.blocks[i]; |
48add0f5a gennvm: Generic N... |
276 |
list_move_tail(&blk->list, &lun->bb_list); |
bdded1552 lightnvm: fix inc... |
277 |
lun->vlun.nr_free_blocks--; |
48add0f5a gennvm: Generic N... |
278 279 280 281 |
} return 0; } |
5e60edb7d lightnvm: rename ... |
282 |
static int gen_block_map(u64 slba, u32 nlb, __le64 *entries, void *private) |
48add0f5a gennvm: Generic N... |
283 284 |
{ struct nvm_dev *dev = private; |
5e60edb7d lightnvm: rename ... |
285 |
struct gen_dev *gn = dev->mp; |
48add0f5a 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 lightnvm: rename ... |
291 |
if (unlikely(elba > dev->total_secs)) { |
5e60edb7d lightnvm: rename ... |
292 293 |
pr_err("gen: L2P data from device is out of bounds! "); |
48add0f5a gennvm: Generic N... |
294 295 296 297 298 |
return -EINVAL; } for (i = 0; i < nlb; i++) { u64 pba = le64_to_cpu(entries[i]); |
4ece44af7 lightnvm: rename ... |
299 |
if (unlikely(pba >= dev->total_secs && pba != U64_MAX)) { |
5e60edb7d lightnvm: rename ... |
300 301 |
pr_err("gen: L2P data entry is out of bounds! "); |
48add0f5a 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 lightnvm: manage ... |
319 |
if (!blk->state) { |
48add0f5a 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 lightnvm: manage ... |
322 |
* block state. The block is assumed to be open. |
48add0f5a gennvm: Generic N... |
323 324 |
*/ list_move_tail(&blk->list, &lun->used_list); |
077d23899 lightnvm: remove ... |
325 |
blk->state = NVM_BLK_ST_TGT; |
48add0f5a gennvm: Generic N... |
326 327 328 329 330 331 |
lun->vlun.nr_free_blocks--; } } return 0; } |
5e60edb7d lightnvm: rename ... |
332 |
static int gen_blocks_init(struct nvm_dev *dev, struct gen_dev *gn) |
48add0f5a 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 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 gennvm: Generic N... |
344 |
|
5e60edb7d lightnvm: rename ... |
345 |
gen_for_each_lun(gn, lun, lun_iter) { |
48add0f5a gennvm: Generic N... |
346 347 |
lun->vlun.blocks = vzalloc(sizeof(struct nvm_block) * dev->blks_per_lun); |
e11903f5d lightnvm: refacto... |
348 349 |
if (!lun->vlun.blocks) { kfree(blks); |
48add0f5a gennvm: Generic N... |
350 |
return -ENOMEM; |
e11903f5d lightnvm: refacto... |
351 |
} |
48add0f5a 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 lightnvm: keep tr... |
362 363 |
if (unlikely(lun_iter == 0 && blk_iter == 0)) { lun->vlun.nr_free_blocks--; |
48add0f5a gennvm: Generic N... |
364 |
continue; |
0b59733b9 lightnvm: keep tr... |
365 |
} |
48add0f5a gennvm: Generic N... |
366 367 368 369 370 |
list_add_tail(&block->list, &lun->free_list); } if (dev->ops->get_bb_tbl) { |
114504698 lightnvm: update ... |
371 372 373 374 |
struct ppa_addr ppa; ppa.ppa = 0; ppa.g.ch = lun->vlun.chnl_id; |
293a6e8e2 lightnvm: fix out... |
375 |
ppa.g.lun = lun->vlun.lun_id; |
114504698 lightnvm: update ... |
376 |
|
e11903f5d lightnvm: refacto... |
377 378 |
ret = nvm_get_bb_tbl(dev, ppa, blks); if (ret) |
5e60edb7d lightnvm: rename ... |
379 380 |
pr_err("gen: could not get BB table "); |
e11903f5d lightnvm: refacto... |
381 |
|
5e60edb7d lightnvm: rename ... |
382 |
ret = gen_block_bb(gn, ppa, blks, nr_blks); |
48add0f5a gennvm: Generic N... |
383 |
if (ret) |
5e60edb7d lightnvm: rename ... |
384 385 |
pr_err("gen: BB table map failed "); |
48add0f5a gennvm: Generic N... |
386 387 |
} } |
29fd20b8e lightnvm: do not ... |
388 |
if ((dev->identity.dom & NVM_RSP_L2P) && dev->ops->get_l2p_tbl) { |
4ece44af7 lightnvm: rename ... |
389 |
ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_secs, |
5e60edb7d lightnvm: rename ... |
390 |
gen_block_map, dev); |
48add0f5a gennvm: Generic N... |
391 |
if (ret) { |
5e60edb7d lightnvm: rename ... |
392 393 394 |
pr_err("gen: could not read L2P table. "); pr_warn("gen: default block initialization"); |
48add0f5a gennvm: Generic N... |
395 396 |
} } |
e11903f5d lightnvm: refacto... |
397 |
kfree(blks); |
48add0f5a gennvm: Generic N... |
398 399 |
return 0; } |
5e60edb7d lightnvm: rename ... |
400 |
static void gen_free(struct nvm_dev *dev) |
8261bd48c lightnvm: free me... |
401 |
{ |
5e60edb7d lightnvm: rename ... |
402 403 |
gen_blocks_free(dev); gen_luns_free(dev); |
8261bd48c lightnvm: free me... |
404 405 406 |
kfree(dev->mp); dev->mp = NULL; } |
5e60edb7d lightnvm: rename ... |
407 |
static int gen_register(struct nvm_dev *dev) |
48add0f5a gennvm: Generic N... |
408 |
{ |
5e60edb7d lightnvm: rename ... |
409 |
struct gen_dev *gn; |
48add0f5a gennvm: Generic N... |
410 |
int ret; |
008b74438 lightnvm: prevent... |
411 412 |
if (!try_module_get(THIS_MODULE)) return -ENODEV; |
5e60edb7d lightnvm: rename ... |
413 |
gn = kzalloc(sizeof(struct gen_dev), GFP_KERNEL); |
48add0f5a gennvm: Generic N... |
414 415 |
if (!gn) return -ENOMEM; |
114504698 lightnvm: update ... |
416 |
gn->dev = dev; |
48add0f5a gennvm: Generic N... |
417 |
gn->nr_luns = dev->nr_luns; |
4c9dacb82 lightnvm: specify... |
418 |
INIT_LIST_HEAD(&gn->area_list); |
b76eb20bb lightnvm: move ta... |
419 420 |
mutex_init(&gn->lock); INIT_LIST_HEAD(&gn->targets); |
48add0f5a gennvm: Generic N... |
421 |
dev->mp = gn; |
5e60edb7d lightnvm: rename ... |
422 |
ret = gen_luns_init(dev, gn); |
48add0f5a gennvm: Generic N... |
423 |
if (ret) { |
5e60edb7d lightnvm: rename ... |
424 425 |
pr_err("gen: could not initialize luns "); |
48add0f5a gennvm: Generic N... |
426 427 |
goto err; } |
5e60edb7d lightnvm: rename ... |
428 |
ret = gen_blocks_init(dev, gn); |
48add0f5a gennvm: Generic N... |
429 |
if (ret) { |
5e60edb7d lightnvm: rename ... |
430 431 |
pr_err("gen: could not initialize blocks "); |
48add0f5a gennvm: Generic N... |
432 433 434 435 436 |
goto err; } return 1; err: |
5e60edb7d lightnvm: rename ... |
437 |
gen_free(dev); |
008b74438 lightnvm: prevent... |
438 |
module_put(THIS_MODULE); |
48add0f5a gennvm: Generic N... |
439 440 |
return ret; } |
5e60edb7d lightnvm: rename ... |
441 |
static void gen_unregister(struct nvm_dev *dev) |
48add0f5a gennvm: Generic N... |
442 |
{ |
b76eb20bb 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 lightnvm: rename ... |
453 |
gen_free(dev); |
008b74438 lightnvm: prevent... |
454 |
module_put(THIS_MODULE); |
48add0f5a gennvm: Generic N... |
455 |
} |
41285fad5 lightnvm: remove ... |
456 |
static struct nvm_block *gen_get_blk(struct nvm_dev *dev, |
48add0f5a 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 lightnvm: remove ... |
462 |
spin_lock(&vlun->lock); |
48add0f5a gennvm: Generic N... |
463 |
if (list_empty(&lun->free_list)) { |
5e60edb7d lightnvm: rename ... |
464 |
pr_err_ratelimited("gen: lun %u have no free pages available", |
48add0f5a gennvm: Generic N... |
465 |
lun->vlun.id); |
48add0f5a gennvm: Generic N... |
466 467 |
goto out; } |
e9b76a80f lightnvm: refacto... |
468 |
if (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks) |
48add0f5a gennvm: Generic N... |
469 |
goto out; |
48add0f5a gennvm: Generic N... |
470 471 |
blk = list_first_entry(&lun->free_list, struct nvm_block, list); |
48add0f5a gennvm: Generic N... |
472 |
|
077d23899 lightnvm: remove ... |
473 474 |
list_move_tail(&blk->list, &lun->used_list); blk->state = NVM_BLK_ST_TGT; |
48add0f5a gennvm: Generic N... |
475 |
lun->vlun.nr_free_blocks--; |
48add0f5a gennvm: Generic N... |
476 |
out: |
e9b76a80f lightnvm: refacto... |
477 |
spin_unlock(&vlun->lock); |
48add0f5a gennvm: Generic N... |
478 479 |
return blk; } |
41285fad5 lightnvm: remove ... |
480 |
static void gen_put_blk(struct nvm_dev *dev, struct nvm_block *blk) |
48add0f5a gennvm: Generic N... |
481 482 483 |
{ struct nvm_lun *vlun = blk->lun; struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun); |
41285fad5 lightnvm: remove ... |
484 |
spin_lock(&vlun->lock); |
077d23899 lightnvm: remove ... |
485 |
if (blk->state & NVM_BLK_ST_TGT) { |
ff0e498bf lightnvm: manage ... |
486 |
list_move_tail(&blk->list, &lun->free_list); |
ff0e498bf 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 gennvm: Generic N... |
490 |
list_move_tail(&blk->list, &lun->bb_list); |
ff0e498bf lightnvm: manage ... |
491 492 |
blk->state = NVM_BLK_ST_BAD; } else { |
48add0f5a gennvm: Generic N... |
493 |
WARN_ON_ONCE(1); |
5e60edb7d lightnvm: rename ... |
494 495 |
pr_err("gen: erroneous block type (%lu -> %u) ", |
ff0e498bf lightnvm: manage ... |
496 |
blk->id, blk->state); |
48add0f5a gennvm: Generic N... |
497 498 |
list_move_tail(&blk->list, &lun->bb_list); } |
48add0f5a gennvm: Generic N... |
499 500 |
spin_unlock(&vlun->lock); } |
5e60edb7d lightnvm: rename ... |
501 |
static void gen_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type) |
48add0f5a gennvm: Generic N... |
502 |
{ |
5e60edb7d lightnvm: rename ... |
503 |
struct gen_dev *gn = dev->mp; |
48add0f5a gennvm: Generic N... |
504 505 |
struct gen_lun *lun; struct nvm_block *blk; |
5e60edb7d lightnvm: rename ... |
506 507 |
pr_debug("gen: ppa (ch: %u lun: %u blk: %u pg: %u) -> %u ", |
04a8aa173 lightnvm: expose ... |
508 |
ppa.g.ch, ppa.g.lun, ppa.g.blk, ppa.g.pg, type); |
a63d5cf20 lightnvm: move re... |
509 |
|
04a8aa173 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 gennvm: Generic N... |
513 |
WARN_ON_ONCE(1); |
5e60edb7d lightnvm: rename ... |
514 |
pr_err("gen: ppa broken (ch: %u > %u lun: %u > %u blk: %u > %u", |
04a8aa173 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 gennvm: Generic N... |
518 519 |
return; } |
24d4a7d72 lightnvm: fix lun... |
520 |
lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun]; |
04a8aa173 lightnvm: expose ... |
521 |
blk = &lun->vlun.blocks[ppa.g.blk]; |
48add0f5a gennvm: Generic N... |
522 523 |
/* will be moved to bb list on put_blk from target */ |
ff0e498bf lightnvm: manage ... |
524 |
blk->state = type; |
48add0f5a gennvm: Generic N... |
525 |
} |
a63d5cf20 lightnvm: move re... |
526 |
/* |
5e60edb7d lightnvm: rename ... |
527 |
* mark block bad in gen. It is expected that the target recovers separately |
a63d5cf20 lightnvm: move re... |
528 |
*/ |
5e60edb7d lightnvm: rename ... |
529 |
static void gen_mark_blk_bad(struct nvm_dev *dev, struct nvm_rq *rqd) |
48add0f5a gennvm: Generic N... |
530 |
{ |
a63d5cf20 lightnvm: move re... |
531 532 533 |
int bit = -1; int max_secs = dev->ops->max_phys_sect; void *comp_bits = &rqd->ppa_status; |
48add0f5a gennvm: Generic N... |
534 |
|
069368e91 lightnvm: move pp... |
535 |
nvm_addr_to_generic_mode(dev, rqd); |
48add0f5a gennvm: Generic N... |
536 537 |
/* look up blocks and mark them as bad */ |
6d5be9590 lightnvm: rename ... |
538 |
if (rqd->nr_ppas == 1) { |
5e60edb7d lightnvm: rename ... |
539 |
gen_mark_blk(dev, rqd->ppa_addr, NVM_BLK_ST_BAD); |
a63d5cf20 lightnvm: move re... |
540 541 542 543 |
return; } while ((bit = find_next_bit(comp_bits, max_secs, bit + 1)) < max_secs) |
5e60edb7d lightnvm: rename ... |
544 |
gen_mark_blk(dev, rqd->ppa_list[bit], NVM_BLK_ST_BAD); |
48add0f5a gennvm: Generic N... |
545 |
} |
5e60edb7d lightnvm: rename ... |
546 |
static void gen_end_io(struct nvm_rq *rqd) |
48add0f5a gennvm: Generic N... |
547 548 |
{ struct nvm_tgt_instance *ins = rqd->ins; |
48add0f5a gennvm: Generic N... |
549 |
|
a63d5cf20 lightnvm: move re... |
550 |
if (rqd->error == NVM_RSP_ERR_FAILWRITE) |
5e60edb7d lightnvm: rename ... |
551 |
gen_mark_blk_bad(rqd->dev, rqd); |
48add0f5a gennvm: Generic N... |
552 |
|
72d256ecc lightnvm: move rq... |
553 |
ins->tt->end_io(rqd); |
91276162d lightnvm: refacto... |
554 |
} |
48add0f5a gennvm: Generic N... |
555 |
|
5e60edb7d lightnvm: rename ... |
556 |
static int gen_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd) |
91276162d 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 lightnvm: rename ... |
565 |
rqd->end_io = gen_end_io; |
91276162d lightnvm: refacto... |
566 |
return dev->ops->submit_io(dev, rqd); |
48add0f5a gennvm: Generic N... |
567 |
} |
5e60edb7d lightnvm: rename ... |
568 |
static int gen_erase_blk(struct nvm_dev *dev, struct nvm_block *blk, |
48add0f5a gennvm: Generic N... |
569 570 |
unsigned long flags) { |
069368e91 lightnvm: move pp... |
571 |
struct ppa_addr addr = block_to_ppa(dev, blk); |
48add0f5a gennvm: Generic N... |
572 |
|
81e681d3f lightnvm: support... |
573 |
return nvm_erase_ppa(dev, &addr, 1); |
48add0f5a gennvm: Generic N... |
574 |
} |
5e60edb7d lightnvm: rename ... |
575 |
static int gen_reserve_lun(struct nvm_dev *dev, int lunid) |
da1e28491 lightnvm: add a b... |
576 577 578 |
{ return test_and_set_bit(lunid, dev->lun_map); } |
5e60edb7d lightnvm: rename ... |
579 |
static void gen_release_lun(struct nvm_dev *dev, int lunid) |
da1e28491 lightnvm: add a b... |
580 581 582 |
{ WARN_ON(!test_and_clear_bit(lunid, dev->lun_map)); } |
5e60edb7d lightnvm: rename ... |
583 |
static struct nvm_lun *gen_get_lun(struct nvm_dev *dev, int lunid) |
48add0f5a gennvm: Generic N... |
584 |
{ |
5e60edb7d lightnvm: rename ... |
585 |
struct gen_dev *gn = dev->mp; |
48add0f5a gennvm: Generic N... |
586 |
|
da1e28491 lightnvm: add a b... |
587 588 |
if (unlikely(lunid >= dev->nr_luns)) return NULL; |
48add0f5a gennvm: Generic N... |
589 590 |
return &gn->luns[lunid].vlun; } |
5e60edb7d lightnvm: rename ... |
591 |
static void gen_lun_info_print(struct nvm_dev *dev) |
48add0f5a gennvm: Generic N... |
592 |
{ |
5e60edb7d lightnvm: rename ... |
593 |
struct gen_dev *gn = dev->mp; |
48add0f5a gennvm: Generic N... |
594 595 |
struct gen_lun *lun; unsigned int i; |
2fde0e482 lightnvm: add fre... |
596 |
|
5e60edb7d lightnvm: rename ... |
597 |
gen_for_each_lun(gn, lun, i) { |
2fde0e482 lightnvm: add fre... |
598 |
spin_lock(&lun->vlun.lock); |
077d23899 lightnvm: remove ... |
599 600 601 |
pr_info("%s: lun%8u\t%u ", dev->name, i, lun->vlun.nr_free_blocks); |
2fde0e482 lightnvm: add fre... |
602 603 604 |
spin_unlock(&lun->vlun.lock); } |
48add0f5a gennvm: Generic N... |
605 |
} |
5e60edb7d lightnvm: rename ... |
606 |
static struct nvmm_type gen = { |
ff0e498bf lightnvm: manage ... |
607 608 |
.name = "gennvm", .version = {0, 1, 0}, |
5e60edb7d lightnvm: rename ... |
609 610 |
.register_mgr = gen_register, .unregister_mgr = gen_unregister, |
48add0f5a gennvm: Generic N... |
611 |
|
b76eb20bb lightnvm: move ta... |
612 613 |
.create_tgt = gen_create_tgt, .remove_tgt = gen_remove_tgt, |
5e60edb7d lightnvm: rename ... |
614 615 |
.get_blk = gen_get_blk, .put_blk = gen_put_blk, |
48add0f5a gennvm: Generic N... |
616 |
|
5e60edb7d lightnvm: rename ... |
617 618 |
.submit_io = gen_submit_io, .erase_blk = gen_erase_blk, |
48add0f5a gennvm: Generic N... |
619 |
|
5e60edb7d lightnvm: rename ... |
620 |
.mark_blk = gen_mark_blk, |
04a8aa173 lightnvm: expose ... |
621 |
|
5e60edb7d 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 lightnvm: specify... |
626 |
|
5e60edb7d lightnvm: rename ... |
627 628 |
.get_area = gen_get_area, .put_area = gen_put_area, |
4c9dacb82 lightnvm: specify... |
629 |
|
48add0f5a gennvm: Generic N... |
630 |
}; |
5e60edb7d lightnvm: rename ... |
631 |
static int __init gen_module_init(void) |
48add0f5a gennvm: Generic N... |
632 |
{ |
5e60edb7d lightnvm: rename ... |
633 |
return nvm_register_mgr(&gen); |
48add0f5a gennvm: Generic N... |
634 |
} |
5e60edb7d lightnvm: rename ... |
635 |
static void gen_module_exit(void) |
48add0f5a gennvm: Generic N... |
636 |
{ |
5e60edb7d lightnvm: rename ... |
637 |
nvm_unregister_mgr(&gen); |
48add0f5a gennvm: Generic N... |
638 |
} |
5e60edb7d lightnvm: rename ... |
639 640 |
module_init(gen_module_init); module_exit(gen_module_exit); |
48add0f5a gennvm: Generic N... |
641 |
MODULE_LICENSE("GPL v2"); |
5e60edb7d lightnvm: rename ... |
642 |
MODULE_DESCRIPTION("General media manager for Open-Channel SSDs"); |