diff options
Diffstat (limited to 'drivers/lightnvm/core.c')
-rw-r--r-- | drivers/lightnvm/core.c | 158 |
1 files changed, 88 insertions, 70 deletions
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index f659e605a406..8f41b245cd55 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c | |||
@@ -74,7 +74,7 @@ EXPORT_SYMBOL(nvm_unregister_target); | |||
74 | void *nvm_dev_dma_alloc(struct nvm_dev *dev, gfp_t mem_flags, | 74 | void *nvm_dev_dma_alloc(struct nvm_dev *dev, gfp_t mem_flags, |
75 | dma_addr_t *dma_handler) | 75 | dma_addr_t *dma_handler) |
76 | { | 76 | { |
77 | return dev->ops->dev_dma_alloc(dev->q, dev->ppalist_pool, mem_flags, | 77 | return dev->ops->dev_dma_alloc(dev, dev->ppalist_pool, mem_flags, |
78 | dma_handler); | 78 | dma_handler); |
79 | } | 79 | } |
80 | EXPORT_SYMBOL(nvm_dev_dma_alloc); | 80 | EXPORT_SYMBOL(nvm_dev_dma_alloc); |
@@ -97,15 +97,47 @@ static struct nvmm_type *nvm_find_mgr_type(const char *name) | |||
97 | return NULL; | 97 | return NULL; |
98 | } | 98 | } |
99 | 99 | ||
100 | struct nvmm_type *nvm_init_mgr(struct nvm_dev *dev) | ||
101 | { | ||
102 | struct nvmm_type *mt; | ||
103 | int ret; | ||
104 | |||
105 | lockdep_assert_held(&nvm_lock); | ||
106 | |||
107 | list_for_each_entry(mt, &nvm_mgrs, list) { | ||
108 | ret = mt->register_mgr(dev); | ||
109 | if (ret < 0) { | ||
110 | pr_err("nvm: media mgr failed to init (%d) on dev %s\n", | ||
111 | ret, dev->name); | ||
112 | return NULL; /* initialization failed */ | ||
113 | } else if (ret > 0) | ||
114 | return mt; | ||
115 | } | ||
116 | |||
117 | return NULL; | ||
118 | } | ||
119 | |||
100 | int nvm_register_mgr(struct nvmm_type *mt) | 120 | int nvm_register_mgr(struct nvmm_type *mt) |
101 | { | 121 | { |
122 | struct nvm_dev *dev; | ||
102 | int ret = 0; | 123 | int ret = 0; |
103 | 124 | ||
104 | down_write(&nvm_lock); | 125 | down_write(&nvm_lock); |
105 | if (nvm_find_mgr_type(mt->name)) | 126 | if (nvm_find_mgr_type(mt->name)) { |
106 | ret = -EEXIST; | 127 | ret = -EEXIST; |
107 | else | 128 | goto finish; |
129 | } else { | ||
108 | list_add(&mt->list, &nvm_mgrs); | 130 | list_add(&mt->list, &nvm_mgrs); |
131 | } | ||
132 | |||
133 | /* try to register media mgr if any device have none configured */ | ||
134 | list_for_each_entry(dev, &nvm_devices, devices) { | ||
135 | if (dev->mt) | ||
136 | continue; | ||
137 | |||
138 | dev->mt = nvm_init_mgr(dev); | ||
139 | } | ||
140 | finish: | ||
109 | up_write(&nvm_lock); | 141 | up_write(&nvm_lock); |
110 | 142 | ||
111 | return ret; | 143 | return ret; |
@@ -160,11 +192,6 @@ int nvm_erase_blk(struct nvm_dev *dev, struct nvm_block *blk) | |||
160 | } | 192 | } |
161 | EXPORT_SYMBOL(nvm_erase_blk); | 193 | EXPORT_SYMBOL(nvm_erase_blk); |
162 | 194 | ||
163 | static void nvm_core_free(struct nvm_dev *dev) | ||
164 | { | ||
165 | kfree(dev); | ||
166 | } | ||
167 | |||
168 | static int nvm_core_init(struct nvm_dev *dev) | 195 | static int nvm_core_init(struct nvm_dev *dev) |
169 | { | 196 | { |
170 | struct nvm_id *id = &dev->identity; | 197 | struct nvm_id *id = &dev->identity; |
@@ -179,12 +206,21 @@ static int nvm_core_init(struct nvm_dev *dev) | |||
179 | dev->sec_size = grp->csecs; | 206 | dev->sec_size = grp->csecs; |
180 | dev->oob_size = grp->sos; | 207 | dev->oob_size = grp->sos; |
181 | dev->sec_per_pg = grp->fpg_sz / grp->csecs; | 208 | dev->sec_per_pg = grp->fpg_sz / grp->csecs; |
182 | dev->addr_mode = id->ppat; | 209 | memcpy(&dev->ppaf, &id->ppaf, sizeof(struct nvm_addr_format)); |
183 | dev->addr_format = id->ppaf; | ||
184 | 210 | ||
185 | dev->plane_mode = NVM_PLANE_SINGLE; | 211 | dev->plane_mode = NVM_PLANE_SINGLE; |
186 | dev->max_rq_size = dev->ops->max_phys_sect * dev->sec_size; | 212 | dev->max_rq_size = dev->ops->max_phys_sect * dev->sec_size; |
187 | 213 | ||
214 | if (grp->mtype != 0) { | ||
215 | pr_err("nvm: memory type not supported\n"); | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | if (grp->fmtype != 0 && grp->fmtype != 1) { | ||
220 | pr_err("nvm: flash type not supported\n"); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | |||
188 | if (grp->mpos & 0x020202) | 224 | if (grp->mpos & 0x020202) |
189 | dev->plane_mode = NVM_PLANE_DOUBLE; | 225 | dev->plane_mode = NVM_PLANE_DOUBLE; |
190 | if (grp->mpos & 0x040404) | 226 | if (grp->mpos & 0x040404) |
@@ -213,21 +249,17 @@ static void nvm_free(struct nvm_dev *dev) | |||
213 | 249 | ||
214 | if (dev->mt) | 250 | if (dev->mt) |
215 | dev->mt->unregister_mgr(dev); | 251 | dev->mt->unregister_mgr(dev); |
216 | |||
217 | nvm_core_free(dev); | ||
218 | } | 252 | } |
219 | 253 | ||
220 | static int nvm_init(struct nvm_dev *dev) | 254 | static int nvm_init(struct nvm_dev *dev) |
221 | { | 255 | { |
222 | struct nvmm_type *mt; | 256 | int ret = -EINVAL; |
223 | int ret = 0; | ||
224 | 257 | ||
225 | if (!dev->q || !dev->ops) | 258 | if (!dev->q || !dev->ops) |
226 | return -EINVAL; | 259 | return ret; |
227 | 260 | ||
228 | if (dev->ops->identity(dev->q, &dev->identity)) { | 261 | if (dev->ops->identity(dev, &dev->identity)) { |
229 | pr_err("nvm: device could not be identified\n"); | 262 | pr_err("nvm: device could not be identified\n"); |
230 | ret = -EINVAL; | ||
231 | goto err; | 263 | goto err; |
232 | } | 264 | } |
233 | 265 | ||
@@ -251,29 +283,12 @@ static int nvm_init(struct nvm_dev *dev) | |||
251 | goto err; | 283 | goto err; |
252 | } | 284 | } |
253 | 285 | ||
254 | /* register with device with a supported manager */ | ||
255 | list_for_each_entry(mt, &nvm_mgrs, list) { | ||
256 | ret = mt->register_mgr(dev); | ||
257 | if (ret < 0) | ||
258 | goto err; /* initialization failed */ | ||
259 | if (ret > 0) { | ||
260 | dev->mt = mt; | ||
261 | break; /* successfully initialized */ | ||
262 | } | ||
263 | } | ||
264 | |||
265 | if (!ret) { | ||
266 | pr_info("nvm: no compatible manager found.\n"); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n", | 286 | pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n", |
271 | dev->name, dev->sec_per_pg, dev->nr_planes, | 287 | dev->name, dev->sec_per_pg, dev->nr_planes, |
272 | dev->pgs_per_blk, dev->blks_per_lun, dev->nr_luns, | 288 | dev->pgs_per_blk, dev->blks_per_lun, dev->nr_luns, |
273 | dev->nr_chnls); | 289 | dev->nr_chnls); |
274 | return 0; | 290 | return 0; |
275 | err: | 291 | err: |
276 | nvm_free(dev); | ||
277 | pr_err("nvm: failed to initialize nvm\n"); | 292 | pr_err("nvm: failed to initialize nvm\n"); |
278 | return ret; | 293 | return ret; |
279 | } | 294 | } |
@@ -308,22 +323,27 @@ int nvm_register(struct request_queue *q, char *disk_name, | |||
308 | if (ret) | 323 | if (ret) |
309 | goto err_init; | 324 | goto err_init; |
310 | 325 | ||
311 | down_write(&nvm_lock); | 326 | if (dev->ops->max_phys_sect > 256) { |
312 | list_add(&dev->devices, &nvm_devices); | 327 | pr_info("nvm: max sectors supported is 256.\n"); |
313 | up_write(&nvm_lock); | 328 | ret = -EINVAL; |
329 | goto err_init; | ||
330 | } | ||
314 | 331 | ||
315 | if (dev->ops->max_phys_sect > 1) { | 332 | if (dev->ops->max_phys_sect > 1) { |
316 | dev->ppalist_pool = dev->ops->create_dma_pool(dev->q, | 333 | dev->ppalist_pool = dev->ops->create_dma_pool(dev, "ppalist"); |
317 | "ppalist"); | ||
318 | if (!dev->ppalist_pool) { | 334 | if (!dev->ppalist_pool) { |
319 | pr_err("nvm: could not create ppa pool\n"); | 335 | pr_err("nvm: could not create ppa pool\n"); |
320 | return -ENOMEM; | 336 | ret = -ENOMEM; |
337 | goto err_init; | ||
321 | } | 338 | } |
322 | } else if (dev->ops->max_phys_sect > 256) { | ||
323 | pr_info("nvm: max sectors supported is 256.\n"); | ||
324 | return -EINVAL; | ||
325 | } | 339 | } |
326 | 340 | ||
341 | /* register device with a supported media manager */ | ||
342 | down_write(&nvm_lock); | ||
343 | dev->mt = nvm_init_mgr(dev); | ||
344 | list_add(&dev->devices, &nvm_devices); | ||
345 | up_write(&nvm_lock); | ||
346 | |||
327 | return 0; | 347 | return 0; |
328 | err_init: | 348 | err_init: |
329 | kfree(dev); | 349 | kfree(dev); |
@@ -333,19 +353,22 @@ EXPORT_SYMBOL(nvm_register); | |||
333 | 353 | ||
334 | void nvm_unregister(char *disk_name) | 354 | void nvm_unregister(char *disk_name) |
335 | { | 355 | { |
336 | struct nvm_dev *dev = nvm_find_nvm_dev(disk_name); | 356 | struct nvm_dev *dev; |
337 | 357 | ||
358 | down_write(&nvm_lock); | ||
359 | dev = nvm_find_nvm_dev(disk_name); | ||
338 | if (!dev) { | 360 | if (!dev) { |
339 | pr_err("nvm: could not find device %s to unregister\n", | 361 | pr_err("nvm: could not find device %s to unregister\n", |
340 | disk_name); | 362 | disk_name); |
363 | up_write(&nvm_lock); | ||
341 | return; | 364 | return; |
342 | } | 365 | } |
343 | 366 | ||
344 | nvm_exit(dev); | ||
345 | |||
346 | down_write(&nvm_lock); | ||
347 | list_del(&dev->devices); | 367 | list_del(&dev->devices); |
348 | up_write(&nvm_lock); | 368 | up_write(&nvm_lock); |
369 | |||
370 | nvm_exit(dev); | ||
371 | kfree(dev); | ||
349 | } | 372 | } |
350 | EXPORT_SYMBOL(nvm_unregister); | 373 | EXPORT_SYMBOL(nvm_unregister); |
351 | 374 | ||
@@ -358,38 +381,24 @@ static int nvm_create_target(struct nvm_dev *dev, | |||
358 | { | 381 | { |
359 | struct nvm_ioctl_create_simple *s = &create->conf.s; | 382 | struct nvm_ioctl_create_simple *s = &create->conf.s; |
360 | struct request_queue *tqueue; | 383 | struct request_queue *tqueue; |
361 | struct nvmm_type *mt; | ||
362 | struct gendisk *tdisk; | 384 | struct gendisk *tdisk; |
363 | struct nvm_tgt_type *tt; | 385 | struct nvm_tgt_type *tt; |
364 | struct nvm_target *t; | 386 | struct nvm_target *t; |
365 | void *targetdata; | 387 | void *targetdata; |
366 | int ret = 0; | ||
367 | 388 | ||
368 | if (!dev->mt) { | 389 | if (!dev->mt) { |
369 | /* register with device with a supported NVM manager */ | 390 | pr_info("nvm: device has no media manager registered.\n"); |
370 | list_for_each_entry(mt, &nvm_mgrs, list) { | 391 | return -ENODEV; |
371 | ret = mt->register_mgr(dev); | ||
372 | if (ret < 0) | ||
373 | return ret; /* initialization failed */ | ||
374 | if (ret > 0) { | ||
375 | dev->mt = mt; | ||
376 | break; /* successfully initialized */ | ||
377 | } | ||
378 | } | ||
379 | |||
380 | if (!ret) { | ||
381 | pr_info("nvm: no compatible nvm manager found.\n"); | ||
382 | return -ENODEV; | ||
383 | } | ||
384 | } | 392 | } |
385 | 393 | ||
394 | down_write(&nvm_lock); | ||
386 | tt = nvm_find_target_type(create->tgttype); | 395 | tt = nvm_find_target_type(create->tgttype); |
387 | if (!tt) { | 396 | if (!tt) { |
388 | pr_err("nvm: target type %s not found\n", create->tgttype); | 397 | pr_err("nvm: target type %s not found\n", create->tgttype); |
398 | up_write(&nvm_lock); | ||
389 | return -EINVAL; | 399 | return -EINVAL; |
390 | } | 400 | } |
391 | 401 | ||
392 | down_write(&nvm_lock); | ||
393 | list_for_each_entry(t, &dev->online_targets, list) { | 402 | list_for_each_entry(t, &dev->online_targets, list) { |
394 | if (!strcmp(create->tgtname, t->disk->disk_name)) { | 403 | if (!strcmp(create->tgtname, t->disk->disk_name)) { |
395 | pr_err("nvm: target name already exists.\n"); | 404 | pr_err("nvm: target name already exists.\n"); |
@@ -457,11 +466,11 @@ static void nvm_remove_target(struct nvm_target *t) | |||
457 | lockdep_assert_held(&nvm_lock); | 466 | lockdep_assert_held(&nvm_lock); |
458 | 467 | ||
459 | del_gendisk(tdisk); | 468 | del_gendisk(tdisk); |
469 | blk_cleanup_queue(q); | ||
470 | |||
460 | if (tt->exit) | 471 | if (tt->exit) |
461 | tt->exit(tdisk->private_data); | 472 | tt->exit(tdisk->private_data); |
462 | 473 | ||
463 | blk_cleanup_queue(q); | ||
464 | |||
465 | put_disk(tdisk); | 474 | put_disk(tdisk); |
466 | 475 | ||
467 | list_del(&t->list); | 476 | list_del(&t->list); |
@@ -473,7 +482,9 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create) | |||
473 | struct nvm_dev *dev; | 482 | struct nvm_dev *dev; |
474 | struct nvm_ioctl_create_simple *s; | 483 | struct nvm_ioctl_create_simple *s; |
475 | 484 | ||
485 | down_write(&nvm_lock); | ||
476 | dev = nvm_find_nvm_dev(create->dev); | 486 | dev = nvm_find_nvm_dev(create->dev); |
487 | up_write(&nvm_lock); | ||
477 | if (!dev) { | 488 | if (!dev) { |
478 | pr_err("nvm: device not found\n"); | 489 | pr_err("nvm: device not found\n"); |
479 | return -EINVAL; | 490 | return -EINVAL; |
@@ -532,7 +543,9 @@ static int nvm_configure_show(const char *val) | |||
532 | return -EINVAL; | 543 | return -EINVAL; |
533 | } | 544 | } |
534 | 545 | ||
546 | down_write(&nvm_lock); | ||
535 | dev = nvm_find_nvm_dev(devname); | 547 | dev = nvm_find_nvm_dev(devname); |
548 | up_write(&nvm_lock); | ||
536 | if (!dev) { | 549 | if (!dev) { |
537 | pr_err("nvm: device not found\n"); | 550 | pr_err("nvm: device not found\n"); |
538 | return -EINVAL; | 551 | return -EINVAL; |
@@ -541,7 +554,7 @@ static int nvm_configure_show(const char *val) | |||
541 | if (!dev->mt) | 554 | if (!dev->mt) |
542 | return 0; | 555 | return 0; |
543 | 556 | ||
544 | dev->mt->free_blocks_print(dev); | 557 | dev->mt->lun_info_print(dev); |
545 | 558 | ||
546 | return 0; | 559 | return 0; |
547 | } | 560 | } |
@@ -677,8 +690,10 @@ static long nvm_ioctl_info(struct file *file, void __user *arg) | |||
677 | info->tgtsize = tgt_iter; | 690 | info->tgtsize = tgt_iter; |
678 | up_write(&nvm_lock); | 691 | up_write(&nvm_lock); |
679 | 692 | ||
680 | if (copy_to_user(arg, info, sizeof(struct nvm_ioctl_info))) | 693 | if (copy_to_user(arg, info, sizeof(struct nvm_ioctl_info))) { |
694 | kfree(info); | ||
681 | return -EFAULT; | 695 | return -EFAULT; |
696 | } | ||
682 | 697 | ||
683 | kfree(info); | 698 | kfree(info); |
684 | return 0; | 699 | return 0; |
@@ -721,8 +736,11 @@ static long nvm_ioctl_get_devices(struct file *file, void __user *arg) | |||
721 | 736 | ||
722 | devices->nr_devices = i; | 737 | devices->nr_devices = i; |
723 | 738 | ||
724 | if (copy_to_user(arg, devices, sizeof(struct nvm_ioctl_get_devices))) | 739 | if (copy_to_user(arg, devices, |
740 | sizeof(struct nvm_ioctl_get_devices))) { | ||
741 | kfree(devices); | ||
725 | return -EFAULT; | 742 | return -EFAULT; |
743 | } | ||
726 | 744 | ||
727 | kfree(devices); | 745 | kfree(devices); |
728 | return 0; | 746 | return 0; |