diff options
Diffstat (limited to 'drivers/lightnvm/gennvm.c')
-rw-r--r-- | drivers/lightnvm/gennvm.c | 105 |
1 files changed, 72 insertions, 33 deletions
diff --git a/drivers/lightnvm/gennvm.c b/drivers/lightnvm/gennvm.c index ae1fb2bdc5f4..f434e89e1c7a 100644 --- a/drivers/lightnvm/gennvm.c +++ b/drivers/lightnvm/gennvm.c | |||
@@ -60,23 +60,27 @@ static int gennvm_luns_init(struct nvm_dev *dev, struct gen_nvm *gn) | |||
60 | lun->vlun.lun_id = i % dev->luns_per_chnl; | 60 | lun->vlun.lun_id = i % dev->luns_per_chnl; |
61 | lun->vlun.chnl_id = i / dev->luns_per_chnl; | 61 | lun->vlun.chnl_id = i / dev->luns_per_chnl; |
62 | lun->vlun.nr_free_blocks = dev->blks_per_lun; | 62 | lun->vlun.nr_free_blocks = dev->blks_per_lun; |
63 | lun->vlun.nr_inuse_blocks = 0; | ||
64 | lun->vlun.nr_bad_blocks = 0; | ||
63 | } | 65 | } |
64 | return 0; | 66 | return 0; |
65 | } | 67 | } |
66 | 68 | ||
67 | static int gennvm_block_bb(u32 lun_id, void *bb_bitmap, unsigned int nr_blocks, | 69 | static int gennvm_block_bb(struct ppa_addr ppa, int nr_blocks, u8 *blks, |
68 | void *private) | 70 | void *private) |
69 | { | 71 | { |
70 | struct gen_nvm *gn = private; | 72 | struct gen_nvm *gn = private; |
71 | struct gen_lun *lun = &gn->luns[lun_id]; | 73 | struct nvm_dev *dev = gn->dev; |
74 | struct gen_lun *lun; | ||
72 | struct nvm_block *blk; | 75 | struct nvm_block *blk; |
73 | int i; | 76 | int i; |
74 | 77 | ||
75 | if (unlikely(bitmap_empty(bb_bitmap, nr_blocks))) | 78 | lun = &gn->luns[(dev->nr_luns * ppa.g.ch) + ppa.g.lun]; |
76 | return 0; | 79 | |
80 | for (i = 0; i < nr_blocks; i++) { | ||
81 | if (blks[i] == 0) | ||
82 | continue; | ||
77 | 83 | ||
78 | i = -1; | ||
79 | while ((i = find_next_bit(bb_bitmap, nr_blocks, i + 1)) < nr_blocks) { | ||
80 | blk = &lun->vlun.blocks[i]; | 84 | blk = &lun->vlun.blocks[i]; |
81 | if (!blk) { | 85 | if (!blk) { |
82 | pr_err("gennvm: BB data is out of bounds.\n"); | 86 | pr_err("gennvm: BB data is out of bounds.\n"); |
@@ -84,6 +88,7 @@ static int gennvm_block_bb(u32 lun_id, void *bb_bitmap, unsigned int nr_blocks, | |||
84 | } | 88 | } |
85 | 89 | ||
86 | list_move_tail(&blk->list, &lun->bb_list); | 90 | list_move_tail(&blk->list, &lun->bb_list); |
91 | lun->vlun.nr_bad_blocks++; | ||
87 | } | 92 | } |
88 | 93 | ||
89 | return 0; | 94 | return 0; |
@@ -136,6 +141,7 @@ static int gennvm_block_map(u64 slba, u32 nlb, __le64 *entries, void *private) | |||
136 | list_move_tail(&blk->list, &lun->used_list); | 141 | list_move_tail(&blk->list, &lun->used_list); |
137 | blk->type = 1; | 142 | blk->type = 1; |
138 | lun->vlun.nr_free_blocks--; | 143 | lun->vlun.nr_free_blocks--; |
144 | lun->vlun.nr_inuse_blocks++; | ||
139 | } | 145 | } |
140 | } | 146 | } |
141 | 147 | ||
@@ -164,22 +170,32 @@ static int gennvm_blocks_init(struct nvm_dev *dev, struct gen_nvm *gn) | |||
164 | block->id = cur_block_id++; | 170 | block->id = cur_block_id++; |
165 | 171 | ||
166 | /* First block is reserved for device */ | 172 | /* First block is reserved for device */ |
167 | if (unlikely(lun_iter == 0 && blk_iter == 0)) | 173 | if (unlikely(lun_iter == 0 && blk_iter == 0)) { |
174 | lun->vlun.nr_free_blocks--; | ||
168 | continue; | 175 | continue; |
176 | } | ||
169 | 177 | ||
170 | list_add_tail(&block->list, &lun->free_list); | 178 | list_add_tail(&block->list, &lun->free_list); |
171 | } | 179 | } |
172 | 180 | ||
173 | if (dev->ops->get_bb_tbl) { | 181 | if (dev->ops->get_bb_tbl) { |
174 | ret = dev->ops->get_bb_tbl(dev->q, lun->vlun.id, | 182 | struct ppa_addr ppa; |
175 | dev->blks_per_lun, gennvm_block_bb, gn); | 183 | |
184 | ppa.ppa = 0; | ||
185 | ppa.g.ch = lun->vlun.chnl_id; | ||
186 | ppa.g.lun = lun->vlun.id; | ||
187 | ppa = generic_to_dev_addr(dev, ppa); | ||
188 | |||
189 | ret = dev->ops->get_bb_tbl(dev, ppa, | ||
190 | dev->blks_per_lun, | ||
191 | gennvm_block_bb, gn); | ||
176 | if (ret) | 192 | if (ret) |
177 | pr_err("gennvm: could not read BB table\n"); | 193 | pr_err("gennvm: could not read BB table\n"); |
178 | } | 194 | } |
179 | } | 195 | } |
180 | 196 | ||
181 | if (dev->ops->get_l2p_tbl) { | 197 | if (dev->ops->get_l2p_tbl) { |
182 | ret = dev->ops->get_l2p_tbl(dev->q, 0, dev->total_pages, | 198 | ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_pages, |
183 | gennvm_block_map, dev); | 199 | gennvm_block_map, dev); |
184 | if (ret) { | 200 | if (ret) { |
185 | pr_err("gennvm: could not read L2P table.\n"); | 201 | pr_err("gennvm: could not read L2P table.\n"); |
@@ -190,15 +206,27 @@ static int gennvm_blocks_init(struct nvm_dev *dev, struct gen_nvm *gn) | |||
190 | return 0; | 206 | return 0; |
191 | } | 207 | } |
192 | 208 | ||
209 | static void gennvm_free(struct nvm_dev *dev) | ||
210 | { | ||
211 | gennvm_blocks_free(dev); | ||
212 | gennvm_luns_free(dev); | ||
213 | kfree(dev->mp); | ||
214 | dev->mp = NULL; | ||
215 | } | ||
216 | |||
193 | static int gennvm_register(struct nvm_dev *dev) | 217 | static int gennvm_register(struct nvm_dev *dev) |
194 | { | 218 | { |
195 | struct gen_nvm *gn; | 219 | struct gen_nvm *gn; |
196 | int ret; | 220 | int ret; |
197 | 221 | ||
222 | if (!try_module_get(THIS_MODULE)) | ||
223 | return -ENODEV; | ||
224 | |||
198 | gn = kzalloc(sizeof(struct gen_nvm), GFP_KERNEL); | 225 | gn = kzalloc(sizeof(struct gen_nvm), GFP_KERNEL); |
199 | if (!gn) | 226 | if (!gn) |
200 | return -ENOMEM; | 227 | return -ENOMEM; |
201 | 228 | ||
229 | gn->dev = dev; | ||
202 | gn->nr_luns = dev->nr_luns; | 230 | gn->nr_luns = dev->nr_luns; |
203 | dev->mp = gn; | 231 | dev->mp = gn; |
204 | 232 | ||
@@ -216,16 +244,15 @@ static int gennvm_register(struct nvm_dev *dev) | |||
216 | 244 | ||
217 | return 1; | 245 | return 1; |
218 | err: | 246 | err: |
219 | kfree(gn); | 247 | gennvm_free(dev); |
248 | module_put(THIS_MODULE); | ||
220 | return ret; | 249 | return ret; |
221 | } | 250 | } |
222 | 251 | ||
223 | static void gennvm_unregister(struct nvm_dev *dev) | 252 | static void gennvm_unregister(struct nvm_dev *dev) |
224 | { | 253 | { |
225 | gennvm_blocks_free(dev); | 254 | gennvm_free(dev); |
226 | gennvm_luns_free(dev); | 255 | module_put(THIS_MODULE); |
227 | kfree(dev->mp); | ||
228 | dev->mp = NULL; | ||
229 | } | 256 | } |
230 | 257 | ||
231 | static struct nvm_block *gennvm_get_blk(struct nvm_dev *dev, | 258 | static struct nvm_block *gennvm_get_blk(struct nvm_dev *dev, |
@@ -240,23 +267,21 @@ static struct nvm_block *gennvm_get_blk(struct nvm_dev *dev, | |||
240 | if (list_empty(&lun->free_list)) { | 267 | if (list_empty(&lun->free_list)) { |
241 | pr_err_ratelimited("gennvm: lun %u have no free pages available", | 268 | pr_err_ratelimited("gennvm: lun %u have no free pages available", |
242 | lun->vlun.id); | 269 | lun->vlun.id); |
243 | spin_unlock(&vlun->lock); | ||
244 | goto out; | 270 | goto out; |
245 | } | 271 | } |
246 | 272 | ||
247 | while (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks) { | 273 | if (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks) |
248 | spin_unlock(&vlun->lock); | ||
249 | goto out; | 274 | goto out; |
250 | } | ||
251 | 275 | ||
252 | blk = list_first_entry(&lun->free_list, struct nvm_block, list); | 276 | blk = list_first_entry(&lun->free_list, struct nvm_block, list); |
253 | list_move_tail(&blk->list, &lun->used_list); | 277 | list_move_tail(&blk->list, &lun->used_list); |
254 | blk->type = 1; | 278 | blk->type = 1; |
255 | 279 | ||
256 | lun->vlun.nr_free_blocks--; | 280 | lun->vlun.nr_free_blocks--; |
281 | lun->vlun.nr_inuse_blocks++; | ||
257 | 282 | ||
258 | spin_unlock(&vlun->lock); | ||
259 | out: | 283 | out: |
284 | spin_unlock(&vlun->lock); | ||
260 | return blk; | 285 | return blk; |
261 | } | 286 | } |
262 | 287 | ||
@@ -271,16 +296,21 @@ static void gennvm_put_blk(struct nvm_dev *dev, struct nvm_block *blk) | |||
271 | case 1: | 296 | case 1: |
272 | list_move_tail(&blk->list, &lun->free_list); | 297 | list_move_tail(&blk->list, &lun->free_list); |
273 | lun->vlun.nr_free_blocks++; | 298 | lun->vlun.nr_free_blocks++; |
299 | lun->vlun.nr_inuse_blocks--; | ||
274 | blk->type = 0; | 300 | blk->type = 0; |
275 | break; | 301 | break; |
276 | case 2: | 302 | case 2: |
277 | list_move_tail(&blk->list, &lun->bb_list); | 303 | list_move_tail(&blk->list, &lun->bb_list); |
304 | lun->vlun.nr_bad_blocks++; | ||
305 | lun->vlun.nr_inuse_blocks--; | ||
278 | break; | 306 | break; |
279 | default: | 307 | default: |
280 | WARN_ON_ONCE(1); | 308 | WARN_ON_ONCE(1); |
281 | pr_err("gennvm: erroneous block type (%lu -> %u)\n", | 309 | pr_err("gennvm: erroneous block type (%lu -> %u)\n", |
282 | blk->id, blk->type); | 310 | blk->id, blk->type); |
283 | list_move_tail(&blk->list, &lun->bb_list); | 311 | list_move_tail(&blk->list, &lun->bb_list); |
312 | lun->vlun.nr_bad_blocks++; | ||
313 | lun->vlun.nr_inuse_blocks--; | ||
284 | } | 314 | } |
285 | 315 | ||
286 | spin_unlock(&vlun->lock); | 316 | spin_unlock(&vlun->lock); |
@@ -292,10 +322,10 @@ static void gennvm_addr_to_generic_mode(struct nvm_dev *dev, struct nvm_rq *rqd) | |||
292 | 322 | ||
293 | if (rqd->nr_pages > 1) { | 323 | if (rqd->nr_pages > 1) { |
294 | for (i = 0; i < rqd->nr_pages; i++) | 324 | for (i = 0; i < rqd->nr_pages; i++) |
295 | rqd->ppa_list[i] = addr_to_generic_mode(dev, | 325 | rqd->ppa_list[i] = dev_to_generic_addr(dev, |
296 | rqd->ppa_list[i]); | 326 | rqd->ppa_list[i]); |
297 | } else { | 327 | } else { |
298 | rqd->ppa_addr = addr_to_generic_mode(dev, rqd->ppa_addr); | 328 | rqd->ppa_addr = dev_to_generic_addr(dev, rqd->ppa_addr); |
299 | } | 329 | } |
300 | } | 330 | } |
301 | 331 | ||
@@ -305,10 +335,10 @@ static void gennvm_generic_to_addr_mode(struct nvm_dev *dev, struct nvm_rq *rqd) | |||
305 | 335 | ||
306 | if (rqd->nr_pages > 1) { | 336 | if (rqd->nr_pages > 1) { |
307 | for (i = 0; i < rqd->nr_pages; i++) | 337 | for (i = 0; i < rqd->nr_pages; i++) |
308 | rqd->ppa_list[i] = generic_to_addr_mode(dev, | 338 | rqd->ppa_list[i] = generic_to_dev_addr(dev, |
309 | rqd->ppa_list[i]); | 339 | rqd->ppa_list[i]); |
310 | } else { | 340 | } else { |
311 | rqd->ppa_addr = generic_to_addr_mode(dev, rqd->ppa_addr); | 341 | rqd->ppa_addr = generic_to_dev_addr(dev, rqd->ppa_addr); |
312 | } | 342 | } |
313 | } | 343 | } |
314 | 344 | ||
@@ -321,7 +351,7 @@ static int gennvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd) | |||
321 | gennvm_generic_to_addr_mode(dev, rqd); | 351 | gennvm_generic_to_addr_mode(dev, rqd); |
322 | 352 | ||
323 | rqd->dev = dev; | 353 | rqd->dev = dev; |
324 | return dev->ops->submit_io(dev->q, rqd); | 354 | return dev->ops->submit_io(dev, rqd); |
325 | } | 355 | } |
326 | 356 | ||
327 | static void gennvm_blk_set_type(struct nvm_dev *dev, struct ppa_addr *ppa, | 357 | static void gennvm_blk_set_type(struct nvm_dev *dev, struct ppa_addr *ppa, |
@@ -354,10 +384,10 @@ static void gennvm_mark_blk_bad(struct nvm_dev *dev, struct nvm_rq *rqd) | |||
354 | { | 384 | { |
355 | int i; | 385 | int i; |
356 | 386 | ||
357 | if (!dev->ops->set_bb) | 387 | if (!dev->ops->set_bb_tbl) |
358 | return; | 388 | return; |
359 | 389 | ||
360 | if (dev->ops->set_bb(dev->q, rqd, 1)) | 390 | if (dev->ops->set_bb_tbl(dev, rqd, 1)) |
361 | return; | 391 | return; |
362 | 392 | ||
363 | gennvm_addr_to_generic_mode(dev, rqd); | 393 | gennvm_addr_to_generic_mode(dev, rqd); |
@@ -425,7 +455,7 @@ static int gennvm_erase_blk(struct nvm_dev *dev, struct nvm_block *blk, | |||
425 | 455 | ||
426 | gennvm_generic_to_addr_mode(dev, &rqd); | 456 | gennvm_generic_to_addr_mode(dev, &rqd); |
427 | 457 | ||
428 | ret = dev->ops->erase_block(dev->q, &rqd); | 458 | ret = dev->ops->erase_block(dev, &rqd); |
429 | 459 | ||
430 | if (plane_cnt) | 460 | if (plane_cnt) |
431 | nvm_dev_dma_free(dev, rqd.ppa_list, rqd.dma_ppa_list); | 461 | nvm_dev_dma_free(dev, rqd.ppa_list, rqd.dma_ppa_list); |
@@ -440,15 +470,24 @@ static struct nvm_lun *gennvm_get_lun(struct nvm_dev *dev, int lunid) | |||
440 | return &gn->luns[lunid].vlun; | 470 | return &gn->luns[lunid].vlun; |
441 | } | 471 | } |
442 | 472 | ||
443 | static void gennvm_free_blocks_print(struct nvm_dev *dev) | 473 | static void gennvm_lun_info_print(struct nvm_dev *dev) |
444 | { | 474 | { |
445 | struct gen_nvm *gn = dev->mp; | 475 | struct gen_nvm *gn = dev->mp; |
446 | struct gen_lun *lun; | 476 | struct gen_lun *lun; |
447 | unsigned int i; | 477 | unsigned int i; |
448 | 478 | ||
449 | gennvm_for_each_lun(gn, lun, i) | 479 | |
450 | pr_info("%s: lun%8u\t%u\n", | 480 | gennvm_for_each_lun(gn, lun, i) { |
451 | dev->name, i, lun->vlun.nr_free_blocks); | 481 | spin_lock(&lun->vlun.lock); |
482 | |||
483 | pr_info("%s: lun%8u\t%u\t%u\t%u\n", | ||
484 | dev->name, i, | ||
485 | lun->vlun.nr_free_blocks, | ||
486 | lun->vlun.nr_inuse_blocks, | ||
487 | lun->vlun.nr_bad_blocks); | ||
488 | |||
489 | spin_unlock(&lun->vlun.lock); | ||
490 | } | ||
452 | } | 491 | } |
453 | 492 | ||
454 | static struct nvmm_type gennvm = { | 493 | static struct nvmm_type gennvm = { |
@@ -466,7 +505,7 @@ static struct nvmm_type gennvm = { | |||
466 | .erase_blk = gennvm_erase_blk, | 505 | .erase_blk = gennvm_erase_blk, |
467 | 506 | ||
468 | .get_lun = gennvm_get_lun, | 507 | .get_lun = gennvm_get_lun, |
469 | .free_blocks_print = gennvm_free_blocks_print, | 508 | .lun_info_print = gennvm_lun_info_print, |
470 | }; | 509 | }; |
471 | 510 | ||
472 | static int __init gennvm_module_init(void) | 511 | static int __init gennvm_module_init(void) |