diff options
author | Wenwei Tao <ww.tao0320@gmail.com> | 2015-11-28 10:49:28 -0500 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2015-11-29 16:34:58 -0500 |
commit | d0a712ceb83ebaea32d520825ee7b997f59b168f (patch) | |
tree | 33c141fe3d4555f86af8eb7275e29e11a1c524d8 /drivers/lightnvm | |
parent | 08236c6bb2980561fba657c58fdc76f2865f236c (diff) |
lightnvm: missing nvm_lock acquire
To avoid race conditions, traverse dev, media manager,
and target lists and also register, unregister entries
to/from them, should be always under the nvm_lock control.
Signed-off-by: Wenwei Tao <ww.tao0320@gmail.com>
Signed-off-by: Matias Bjørling <m@bjorling.me>
Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'drivers/lightnvm')
-rw-r--r-- | drivers/lightnvm/core.c | 75 |
1 files changed, 42 insertions, 33 deletions
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index ea6dba530f0a..86ce887b2ed6 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c | |||
@@ -123,6 +123,26 @@ void nvm_unregister_mgr(struct nvmm_type *mt) | |||
123 | } | 123 | } |
124 | EXPORT_SYMBOL(nvm_unregister_mgr); | 124 | EXPORT_SYMBOL(nvm_unregister_mgr); |
125 | 125 | ||
126 | /* register with device with a supported manager */ | ||
127 | static int register_mgr(struct nvm_dev *dev) | ||
128 | { | ||
129 | struct nvmm_type *mt; | ||
130 | int ret = 0; | ||
131 | |||
132 | list_for_each_entry(mt, &nvm_mgrs, list) { | ||
133 | ret = mt->register_mgr(dev); | ||
134 | if (ret > 0) { | ||
135 | dev->mt = mt; | ||
136 | break; /* successfully initialized */ | ||
137 | } | ||
138 | } | ||
139 | |||
140 | if (!ret) | ||
141 | pr_info("nvm: no compatible nvm manager found.\n"); | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
126 | static struct nvm_dev *nvm_find_nvm_dev(const char *name) | 146 | static struct nvm_dev *nvm_find_nvm_dev(const char *name) |
127 | { | 147 | { |
128 | struct nvm_dev *dev; | 148 | struct nvm_dev *dev; |
@@ -221,7 +241,6 @@ static void nvm_free(struct nvm_dev *dev) | |||
221 | 241 | ||
222 | static int nvm_init(struct nvm_dev *dev) | 242 | static int nvm_init(struct nvm_dev *dev) |
223 | { | 243 | { |
224 | struct nvmm_type *mt; | ||
225 | int ret = -EINVAL; | 244 | int ret = -EINVAL; |
226 | 245 | ||
227 | if (!dev->q || !dev->ops) | 246 | if (!dev->q || !dev->ops) |
@@ -252,21 +271,13 @@ static int nvm_init(struct nvm_dev *dev) | |||
252 | goto err; | 271 | goto err; |
253 | } | 272 | } |
254 | 273 | ||
255 | /* register with device with a supported manager */ | 274 | down_write(&nvm_lock); |
256 | list_for_each_entry(mt, &nvm_mgrs, list) { | 275 | ret = register_mgr(dev); |
257 | ret = mt->register_mgr(dev); | 276 | up_write(&nvm_lock); |
258 | if (ret < 0) | 277 | if (ret < 0) |
259 | goto err; /* initialization failed */ | 278 | goto err; |
260 | if (ret > 0) { | 279 | if (!ret) |
261 | dev->mt = mt; | ||
262 | break; /* successfully initialized */ | ||
263 | } | ||
264 | } | ||
265 | |||
266 | if (!ret) { | ||
267 | pr_info("nvm: no compatible manager found.\n"); | ||
268 | return 0; | 280 | return 0; |
269 | } | ||
270 | 281 | ||
271 | pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n", | 282 | pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n", |
272 | dev->name, dev->sec_per_pg, dev->nr_planes, | 283 | dev->name, dev->sec_per_pg, dev->nr_planes, |
@@ -337,15 +348,17 @@ EXPORT_SYMBOL(nvm_register); | |||
337 | 348 | ||
338 | void nvm_unregister(char *disk_name) | 349 | void nvm_unregister(char *disk_name) |
339 | { | 350 | { |
340 | struct nvm_dev *dev = nvm_find_nvm_dev(disk_name); | 351 | struct nvm_dev *dev; |
341 | 352 | ||
353 | down_write(&nvm_lock); | ||
354 | dev = nvm_find_nvm_dev(disk_name); | ||
342 | if (!dev) { | 355 | if (!dev) { |
343 | pr_err("nvm: could not find device %s to unregister\n", | 356 | pr_err("nvm: could not find device %s to unregister\n", |
344 | disk_name); | 357 | disk_name); |
358 | up_write(&nvm_lock); | ||
345 | return; | 359 | return; |
346 | } | 360 | } |
347 | 361 | ||
348 | down_write(&nvm_lock); | ||
349 | list_del(&dev->devices); | 362 | list_del(&dev->devices); |
350 | up_write(&nvm_lock); | 363 | up_write(&nvm_lock); |
351 | 364 | ||
@@ -363,38 +376,30 @@ static int nvm_create_target(struct nvm_dev *dev, | |||
363 | { | 376 | { |
364 | struct nvm_ioctl_create_simple *s = &create->conf.s; | 377 | struct nvm_ioctl_create_simple *s = &create->conf.s; |
365 | struct request_queue *tqueue; | 378 | struct request_queue *tqueue; |
366 | struct nvmm_type *mt; | ||
367 | struct gendisk *tdisk; | 379 | struct gendisk *tdisk; |
368 | struct nvm_tgt_type *tt; | 380 | struct nvm_tgt_type *tt; |
369 | struct nvm_target *t; | 381 | struct nvm_target *t; |
370 | void *targetdata; | 382 | void *targetdata; |
371 | int ret = 0; | 383 | int ret = 0; |
372 | 384 | ||
385 | down_write(&nvm_lock); | ||
373 | if (!dev->mt) { | 386 | if (!dev->mt) { |
374 | /* register with device with a supported NVM manager */ | 387 | ret = register_mgr(dev); |
375 | list_for_each_entry(mt, &nvm_mgrs, list) { | 388 | if (!ret) |
376 | ret = mt->register_mgr(dev); | 389 | ret = -ENODEV; |
377 | if (ret < 0) | 390 | if (ret < 0) { |
378 | return ret; /* initialization failed */ | 391 | up_write(&nvm_lock); |
379 | if (ret > 0) { | 392 | return ret; |
380 | dev->mt = mt; | ||
381 | break; /* successfully initialized */ | ||
382 | } | ||
383 | } | ||
384 | |||
385 | if (!ret) { | ||
386 | pr_info("nvm: no compatible nvm manager found.\n"); | ||
387 | return -ENODEV; | ||
388 | } | 393 | } |
389 | } | 394 | } |
390 | 395 | ||
391 | tt = nvm_find_target_type(create->tgttype); | 396 | tt = nvm_find_target_type(create->tgttype); |
392 | if (!tt) { | 397 | if (!tt) { |
393 | pr_err("nvm: target type %s not found\n", create->tgttype); | 398 | pr_err("nvm: target type %s not found\n", create->tgttype); |
399 | up_write(&nvm_lock); | ||
394 | return -EINVAL; | 400 | return -EINVAL; |
395 | } | 401 | } |
396 | 402 | ||
397 | down_write(&nvm_lock); | ||
398 | list_for_each_entry(t, &dev->online_targets, list) { | 403 | list_for_each_entry(t, &dev->online_targets, list) { |
399 | if (!strcmp(create->tgtname, t->disk->disk_name)) { | 404 | if (!strcmp(create->tgtname, t->disk->disk_name)) { |
400 | pr_err("nvm: target name already exists.\n"); | 405 | pr_err("nvm: target name already exists.\n"); |
@@ -478,7 +483,9 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create) | |||
478 | struct nvm_dev *dev; | 483 | struct nvm_dev *dev; |
479 | struct nvm_ioctl_create_simple *s; | 484 | struct nvm_ioctl_create_simple *s; |
480 | 485 | ||
486 | down_write(&nvm_lock); | ||
481 | dev = nvm_find_nvm_dev(create->dev); | 487 | dev = nvm_find_nvm_dev(create->dev); |
488 | up_write(&nvm_lock); | ||
482 | if (!dev) { | 489 | if (!dev) { |
483 | pr_err("nvm: device not found\n"); | 490 | pr_err("nvm: device not found\n"); |
484 | return -EINVAL; | 491 | return -EINVAL; |
@@ -537,7 +544,9 @@ static int nvm_configure_show(const char *val) | |||
537 | return -EINVAL; | 544 | return -EINVAL; |
538 | } | 545 | } |
539 | 546 | ||
547 | down_write(&nvm_lock); | ||
540 | dev = nvm_find_nvm_dev(devname); | 548 | dev = nvm_find_nvm_dev(devname); |
549 | up_write(&nvm_lock); | ||
541 | if (!dev) { | 550 | if (!dev) { |
542 | pr_err("nvm: device not found\n"); | 551 | pr_err("nvm: device not found\n"); |
543 | return -EINVAL; | 552 | return -EINVAL; |