aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtd_blkdevs.c
diff options
context:
space:
mode:
authorMaxim Levitsky <maximlevitsky@gmail.com>2010-10-15 11:20:43 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2010-10-24 20:28:30 -0400
commit008c751ec78587dd9b48bb62d4b10d616554fea2 (patch)
tree1334d9fd01dee2c0c4151824bd42886bc66cecd4 /drivers/mtd/mtd_blkdevs.c
parentebd71e3a4861849054751779ff5ccd3fb29a1e0a (diff)
mtd: allow to unload the mtdtrans module if its block devices aren't open
Now it once again possible to remove mtdtrans module. You still need to ensure that block devices of that module aren't mounted. This is due to the fact that as long as a block device is open, it still exists, therefore if we were to allow module removal, this block device might became used again. This time in addition to code review, I also made the code pass some torture tests like module reload in a loop + read in a loop + card insert/removal all at same time. The blktrans_open/blktrans_release don't take the mtd table lock because: While device is added (that includes execution of add_mtd_blktrans_dev) the lock is already taken Now suppose the device will never be removed. In this case even if we have changes in mtd table, the entry that we need will stay exactly the same. (Note that we don't look at table at all, just following private pointer of block device). Now suppose that someone tries to remove the mtd device. This will be propagated to trans driver which _ought_ to call del_mtd_blktrans_dev which will take the per device lock, release the mtd device and set trans->mtd = NULL. >From this point on, following opens won't even be able to know anything about that mtd device (which at that point is likely not to exist) Also the same care is taken not to trip over NULL mtd pointer in blktrans_dev_release. Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com> Tested-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd/mtd_blkdevs.c')
-rw-r--r--drivers/mtd/mtd_blkdevs.c52
1 files changed, 24 insertions, 28 deletions
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 62e68707b07f..352831baf785 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -176,7 +176,7 @@ static void mtd_blktrans_request(struct request_queue *rq)
176static int blktrans_open(struct block_device *bdev, fmode_t mode) 176static int blktrans_open(struct block_device *bdev, fmode_t mode)
177{ 177{
178 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 178 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
179 int ret; 179 int ret = 0;
180 180
181 if (!dev) 181 if (!dev)
182 return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ 182 return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
@@ -184,17 +184,17 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
184 lock_kernel(); 184 lock_kernel();
185 mutex_lock(&dev->lock); 185 mutex_lock(&dev->lock);
186 186
187 if (!dev->mtd) { 187 if (dev->open++)
188 ret = -ENXIO;
189 goto unlock; 188 goto unlock;
190 }
191 189
192 ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; 190 kref_get(&dev->ref);
191 __module_get(dev->tr->owner);
192
193 if (dev->mtd) {
194 ret = dev->tr->open ? dev->tr->open(dev) : 0;
195 __get_mtd_device(dev->mtd);
196 }
193 197
194 /* Take another reference on the device so it won't go away till
195 last release */
196 if (!ret)
197 kref_get(&dev->ref);
198unlock: 198unlock:
199 mutex_unlock(&dev->lock); 199 mutex_unlock(&dev->lock);
200 blktrans_dev_put(dev); 200 blktrans_dev_put(dev);
@@ -205,7 +205,7 @@ unlock:
205static int blktrans_release(struct gendisk *disk, fmode_t mode) 205static int blktrans_release(struct gendisk *disk, fmode_t mode)
206{ 206{
207 struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); 207 struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
208 int ret = -ENXIO; 208 int ret = 0;
209 209
210 if (!dev) 210 if (!dev)
211 return ret; 211 return ret;
@@ -213,13 +213,16 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode)
213 lock_kernel(); 213 lock_kernel();
214 mutex_lock(&dev->lock); 214 mutex_lock(&dev->lock);
215 215
216 /* Release one reference, we sure its not the last one here*/ 216 if (--dev->open)
217 kref_put(&dev->ref, blktrans_dev_release);
218
219 if (!dev->mtd)
220 goto unlock; 217 goto unlock;
221 218
222 ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; 219 kref_put(&dev->ref, blktrans_dev_release);
220 module_put(dev->tr->owner);
221
222 if (dev->mtd) {
223 ret = dev->tr->release ? dev->tr->release(dev) : 0;
224 __put_mtd_device(dev->mtd);
225 }
223unlock: 226unlock:
224 mutex_unlock(&dev->lock); 227 mutex_unlock(&dev->lock);
225 blktrans_dev_put(dev); 228 blktrans_dev_put(dev);
@@ -385,9 +388,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
385 388
386 gd->queue = new->rq; 389 gd->queue = new->rq;
387 390
388 __get_mtd_device(new->mtd);
389 __module_get(tr->owner);
390
391 /* Create processing thread */ 391 /* Create processing thread */
392 /* TODO: workqueue ? */ 392 /* TODO: workqueue ? */
393 new->thread = kthread_run(mtd_blktrans_thread, new, 393 new->thread = kthread_run(mtd_blktrans_thread, new,
@@ -410,8 +410,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
410 } 410 }
411 return 0; 411 return 0;
412error4: 412error4:
413 module_put(tr->owner);
414 __put_mtd_device(new->mtd);
415 blk_cleanup_queue(new->rq); 413 blk_cleanup_queue(new->rq);
416error3: 414error3:
417 put_disk(new->disk); 415 put_disk(new->disk);
@@ -448,17 +446,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
448 blk_start_queue(old->rq); 446 blk_start_queue(old->rq);
449 spin_unlock_irqrestore(&old->queue_lock, flags); 447 spin_unlock_irqrestore(&old->queue_lock, flags);
450 448
451 /* Ask trans driver for release to the mtd device */ 449 /* If the device is currently open, tell trans driver to close it,
450 then put mtd device, and don't touch it again */
452 mutex_lock(&old->lock); 451 mutex_lock(&old->lock);
453 if (old->open && old->tr->release) { 452 if (old->open) {
454 old->tr->release(old); 453 if (old->tr->release)
455 old->open = 0; 454 old->tr->release(old);
455 __put_mtd_device(old->mtd);
456 } 456 }
457 457
458 __put_mtd_device(old->mtd);
459 module_put(old->tr->owner);
460
461 /* At that point, we don't touch the mtd anymore */
462 old->mtd = NULL; 458 old->mtd = NULL;
463 459
464 mutex_unlock(&old->lock); 460 mutex_unlock(&old->lock);