Skip to content

Commit

Permalink
mtd: allow to unload the mtdtrans module if its block devices aren't …
Browse files Browse the repository at this point in the history
…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 <[email protected]>
Tested-by: Artem Bityutskiy <[email protected]>
Signed-off-by: David Woodhouse <[email protected]>
  • Loading branch information
maximlevitsky authored and David Woodhouse committed Oct 25, 2010
1 parent ebd71e3 commit 008c751
Showing 1 changed file with 24 additions and 28 deletions.
52 changes: 24 additions & 28 deletions drivers/mtd/mtd_blkdevs.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,25 +176,25 @@ static void mtd_blktrans_request(struct request_queue *rq)
static int blktrans_open(struct block_device *bdev, fmode_t mode)
{
struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
int ret;
int ret = 0;

if (!dev)
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/

lock_kernel();
mutex_lock(&dev->lock);

if (!dev->mtd) {
ret = -ENXIO;
if (dev->open++)
goto unlock;
}

ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
kref_get(&dev->ref);
__module_get(dev->tr->owner);

if (dev->mtd) {
ret = dev->tr->open ? dev->tr->open(dev) : 0;
__get_mtd_device(dev->mtd);
}

/* Take another reference on the device so it won't go away till
last release */
if (!ret)
kref_get(&dev->ref);
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
Expand All @@ -205,21 +205,24 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
static int blktrans_release(struct gendisk *disk, fmode_t mode)
{
struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
int ret = -ENXIO;
int ret = 0;

if (!dev)
return ret;

lock_kernel();
mutex_lock(&dev->lock);

/* Release one reference, we sure its not the last one here*/
kref_put(&dev->ref, blktrans_dev_release);

if (!dev->mtd)
if (--dev->open)
goto unlock;

ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
kref_put(&dev->ref, blktrans_dev_release);
module_put(dev->tr->owner);

if (dev->mtd) {
ret = dev->tr->release ? dev->tr->release(dev) : 0;
__put_mtd_device(dev->mtd);
}
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
Expand Down Expand Up @@ -385,9 +388,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)

gd->queue = new->rq;

__get_mtd_device(new->mtd);
__module_get(tr->owner);

/* Create processing thread */
/* TODO: workqueue ? */
new->thread = kthread_run(mtd_blktrans_thread, new,
Expand All @@ -410,8 +410,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
}
return 0;
error4:
module_put(tr->owner);
__put_mtd_device(new->mtd);
blk_cleanup_queue(new->rq);
error3:
put_disk(new->disk);
Expand Down Expand Up @@ -448,17 +446,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
blk_start_queue(old->rq);
spin_unlock_irqrestore(&old->queue_lock, flags);

/* Ask trans driver for release to the mtd device */
/* If the device is currently open, tell trans driver to close it,
then put mtd device, and don't touch it again */
mutex_lock(&old->lock);
if (old->open && old->tr->release) {
old->tr->release(old);
old->open = 0;
if (old->open) {
if (old->tr->release)
old->tr->release(old);
__put_mtd_device(old->mtd);
}

__put_mtd_device(old->mtd);
module_put(old->tr->owner);

/* At that point, we don't touch the mtd anymore */
old->mtd = NULL;

mutex_unlock(&old->lock);
Expand Down

0 comments on commit 008c751

Please sign in to comment.