diff options
Diffstat (limited to 'drivers/mtd/mtd_blkdevs.c')
| -rw-r--r-- | drivers/mtd/mtd_blkdevs.c | 175 |
1 files changed, 104 insertions, 71 deletions
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 62e68707b07..43e30992a36 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -29,11 +29,8 @@ #include <linux/blkdev.h> #include <linux/blkpg.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include <linux/hdreg.h> -#include <linux/init.h> #include <linux/mutex.h> -#include <linux/kthread.h> #include <asm/uaccess.h> #include "mtdcore.h" @@ -41,7 +38,7 @@ static LIST_HEAD(blktrans_majors); static DEFINE_MUTEX(blktrans_ref_mutex); -void blktrans_dev_release(struct kref *kref) +static void blktrans_dev_release(struct kref *kref) { struct mtd_blktrans_dev *dev = container_of(kref, struct mtd_blktrans_dev, ref); @@ -68,7 +65,7 @@ unlock: return dev; } -void blktrans_dev_put(struct mtd_blktrans_dev *dev) +static void blktrans_dev_put(struct mtd_blktrans_dev *dev) { mutex_lock(&blktrans_ref_mutex); kref_put(&dev->ref, blktrans_dev_release); @@ -85,12 +82,14 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, block = blk_rq_pos(req) << 9 >> tr->blkshift; nsect = blk_rq_cur_bytes(req) >> tr->blkshift; - - buf = req->buffer; + buf = bio_data(req->bio); if (req->cmd_type != REQ_TYPE_FS) return -EIO; + if (req->cmd_flags & REQ_FLUSH) + return tr->flush(dev); + if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) return -EIO; @@ -120,23 +119,42 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, } } -static int mtd_blktrans_thread(void *arg) +int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev) +{ + return dev->bg_stop; +} +EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background); + +static void mtd_blktrans_work(struct work_struct *work) { - struct mtd_blktrans_dev *dev = arg; + struct mtd_blktrans_dev *dev = + container_of(work, struct mtd_blktrans_dev, work); + struct mtd_blktrans_ops *tr = dev->tr; struct request_queue *rq = dev->rq; struct request *req = NULL; + int background_done = 0; spin_lock_irq(rq->queue_lock); - while (!kthread_should_stop()) { + while (1) { int res; + dev->bg_stop = false; if (!req && !(req = blk_fetch_request(rq))) { - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(rq->queue_lock); - schedule(); - spin_lock_irq(rq->queue_lock); - continue; + if (tr->background && !background_done) { + spin_unlock_irq(rq->queue_lock); + mutex_lock(&dev->lock); + tr->background(dev); + mutex_unlock(&dev->lock); + spin_lock_irq(rq->queue_lock); + /* + * Do background processing just once per idle + * period. + */ + background_done = !dev->bg_stop; + continue; + } + break; } spin_unlock_irq(rq->queue_lock); @@ -149,14 +167,14 @@ static int mtd_blktrans_thread(void *arg) if (!__blk_end_request_cur(req, res)) req = NULL; + + background_done = 0; } if (req) __blk_end_request_all(req, -EIO); spin_unlock_irq(rq->queue_lock); - - return 0; } static void mtd_blktrans_request(struct request_queue *rq) @@ -170,61 +188,79 @@ static void mtd_blktrans_request(struct request_queue *rq) while ((req = blk_fetch_request(rq)) != NULL) __blk_end_request_all(req, -ENODEV); else - wake_up_process(dev->thread); + queue_work(dev->wq, &dev->work); } 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; + + kref_get(&dev->ref); + __module_get(dev->tr->owner); + + if (!dev->mtd) + goto unlock; + + if (dev->tr->open) { + ret = dev->tr->open(dev); + if (ret) + goto error_put; } - ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; + ret = __get_mtd_device(dev->mtd); + if (ret) + goto error_release; + dev->file_mode = mode; - /* Take another reference on the device so it won't go away till - last release */ - if (!ret) - kref_get(&dev->ref); unlock: + dev->open++; + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; + +error_release: + if (dev->tr->release) + dev->tr->release(dev); +error_put: + module_put(dev->tr->owner); + kref_put(&dev->ref, blktrans_dev_release); mutex_unlock(&dev->lock); blktrans_dev_put(dev); - unlock_kernel(); return ret; } -static int blktrans_release(struct gendisk *disk, fmode_t mode) +static void blktrans_release(struct gendisk *disk, fmode_t mode) { struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); - int ret = -ENXIO; if (!dev) - return ret; + return; - 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) { + if (dev->tr->release) + dev->tr->release(dev); + __put_mtd_device(dev->mtd); + } unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); - unlock_kernel(); - return ret; } static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) @@ -256,7 +292,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, if (!dev) return ret; - lock_kernel(); mutex_lock(&dev->lock); if (!dev->mtd) @@ -271,12 +306,11 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, } unlock: mutex_unlock(&dev->lock); - unlock_kernel(); blktrans_dev_put(dev); return ret; } -static const struct block_device_operations mtd_blktrans_ops = { +static const struct block_device_operations mtd_block_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, @@ -352,7 +386,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->private_data = new; gd->major = tr->major; gd->first_minor = (new->devnum) << tr->part_bits; - gd->fops = &mtd_blktrans_ops; + gd->fops = &mtd_block_ops; if (tr->part_bits) if (new->devnum < 26) @@ -376,26 +410,28 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) if (!new->rq) goto error3; + if (tr->flush) + blk_queue_flush(new->rq, REQ_FLUSH); + new->rq->queuedata = new; blk_queue_logical_block_size(new->rq, tr->blksize); - if (tr->discard) - queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, - new->rq); + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq); - gd->queue = new->rq; + if (tr->discard) { + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq); + new->rq->limits.max_discard_sectors = UINT_MAX; + } - __get_mtd_device(new->mtd); - __module_get(tr->owner); + gd->queue = new->rq; - /* Create processing thread */ - /* TODO: workqueue ? */ - new->thread = kthread_run(mtd_blktrans_thread, new, - "%s%d", tr->name, new->mtd->index); - if (IS_ERR(new->thread)) { - ret = PTR_ERR(new->thread); + /* Create processing workqueue */ + new->wq = alloc_workqueue("%s%d", 0, 0, + tr->name, new->mtd->index); + if (!new->wq) goto error4; - } + INIT_WORK(&new->work, mtd_blktrans_work); + gd->driverfs_dev = &new->mtd->dev; if (new->readonly) @@ -410,15 +446,12 @@ 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); error2: list_del(&new->list); error1: - kfree(new); return ret; } @@ -438,9 +471,8 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) /* Stop new requests to arrive */ del_gendisk(old->disk); - - /* Stop the thread */ - kthread_stop(old->thread); + /* Stop workqueue. This will perform any pending request. */ + destroy_workqueue(old->wq); /* Kill current requests */ spin_lock_irqsave(&old->queue_lock, flags); @@ -448,17 +480,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); @@ -508,13 +538,16 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) mutex_lock(&mtd_table_mutex); ret = register_blkdev(tr->major, tr->name); - if (ret) { + if (ret < 0) { printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", tr->name, tr->major, ret); mutex_unlock(&mtd_table_mutex); return ret; } + if (ret) + tr->major = ret; + tr->blkshift = ffs(tr->blksize) - 1; INIT_LIST_HEAD(&tr->devs); |
