11da177e4SLinus Torvalds /* 2a1452a37SDavid Woodhouse * Interface to Linux block layer for MTD 'translation layers'. 31da177e4SLinus Torvalds * 4a1452a37SDavid Woodhouse * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org> 5a1452a37SDavid Woodhouse * 6a1452a37SDavid Woodhouse * This program is free software; you can redistribute it and/or modify 7a1452a37SDavid Woodhouse * it under the terms of the GNU General Public License as published by 8a1452a37SDavid Woodhouse * the Free Software Foundation; either version 2 of the License, or 9a1452a37SDavid Woodhouse * (at your option) any later version. 10a1452a37SDavid Woodhouse * 11a1452a37SDavid Woodhouse * This program is distributed in the hope that it will be useful, 12a1452a37SDavid Woodhouse * but WITHOUT ANY WARRANTY; without even the implied warranty of 13a1452a37SDavid Woodhouse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14a1452a37SDavid Woodhouse * GNU General Public License for more details. 15a1452a37SDavid Woodhouse * 16a1452a37SDavid Woodhouse * You should have received a copy of the GNU General Public License 17a1452a37SDavid Woodhouse * along with this program; if not, write to the Free Software 18a1452a37SDavid Woodhouse * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/kernel.h> 231da177e4SLinus Torvalds #include <linux/slab.h> 241da177e4SLinus Torvalds #include <linux/module.h> 251da177e4SLinus Torvalds #include <linux/list.h> 261da177e4SLinus Torvalds #include <linux/fs.h> 271da177e4SLinus Torvalds #include <linux/mtd/blktrans.h> 281da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 291da177e4SLinus Torvalds #include <linux/blkdev.h> 301da177e4SLinus Torvalds #include <linux/blkpg.h> 311da177e4SLinus Torvalds #include <linux/spinlock.h> 321da177e4SLinus Torvalds #include <linux/hdreg.h> 3348b19268SIngo Molnar #include <linux/mutex.h> 347c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 351da177e4SLinus Torvalds 36356d70f1SBen Dooks #include "mtdcore.h" 371da177e4SLinus Torvalds 38356d70f1SBen Dooks static LIST_HEAD(blktrans_majors); 39048d8719SMaxim Levitsky static DEFINE_MUTEX(blktrans_ref_mutex); 40048d8719SMaxim Levitsky 417f53f12fSH Hartley Sweeten static void blktrans_dev_release(struct kref *kref) 42048d8719SMaxim Levitsky { 43048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev = 44048d8719SMaxim Levitsky container_of(kref, struct mtd_blktrans_dev, ref); 45048d8719SMaxim Levitsky 46048d8719SMaxim Levitsky dev->disk->private_data = NULL; 47e4d64cabSMaxim Levitsky blk_cleanup_queue(dev->rq); 48048d8719SMaxim Levitsky put_disk(dev->disk); 49048d8719SMaxim Levitsky list_del(&dev->list); 50048d8719SMaxim Levitsky kfree(dev); 51048d8719SMaxim Levitsky } 52048d8719SMaxim Levitsky 53048d8719SMaxim Levitsky static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) 54048d8719SMaxim Levitsky { 55048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev; 56048d8719SMaxim Levitsky 57048d8719SMaxim Levitsky mutex_lock(&blktrans_ref_mutex); 58048d8719SMaxim Levitsky dev = disk->private_data; 59048d8719SMaxim Levitsky 60048d8719SMaxim Levitsky if (!dev) 61048d8719SMaxim Levitsky goto unlock; 62048d8719SMaxim Levitsky kref_get(&dev->ref); 63048d8719SMaxim Levitsky unlock: 64048d8719SMaxim Levitsky mutex_unlock(&blktrans_ref_mutex); 65048d8719SMaxim Levitsky return dev; 66048d8719SMaxim Levitsky } 67048d8719SMaxim Levitsky 687f53f12fSH Hartley Sweeten static void blktrans_dev_put(struct mtd_blktrans_dev *dev) 69048d8719SMaxim Levitsky { 70048d8719SMaxim Levitsky mutex_lock(&blktrans_ref_mutex); 71048d8719SMaxim Levitsky kref_put(&dev->ref, blktrans_dev_release); 72048d8719SMaxim Levitsky mutex_unlock(&blktrans_ref_mutex); 73048d8719SMaxim Levitsky } 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds 762a842acaSChristoph Hellwig static blk_status_t do_blktrans_request(struct mtd_blktrans_ops *tr, 771da177e4SLinus Torvalds struct mtd_blktrans_dev *dev, 781da177e4SLinus Torvalds struct request *req) 791da177e4SLinus Torvalds { 801da177e4SLinus Torvalds unsigned long block, nsect; 811da177e4SLinus Torvalds char *buf; 821da177e4SLinus Torvalds 8383096ebfSTejun Heo block = blk_rq_pos(req) << 9 >> tr->blkshift; 841011c1b9STejun Heo nsect = blk_rq_cur_bytes(req) >> tr->blkshift; 85b4f42e28SJens Axboe buf = bio_data(req->bio); 861da177e4SLinus Torvalds 872a842acaSChristoph Hellwig if (req_op(req) == REQ_OP_FLUSH) { 882a842acaSChristoph Hellwig if (tr->flush(dev)) 892a842acaSChristoph Hellwig return BLK_STS_IOERR; 902a842acaSChristoph Hellwig return BLK_STS_OK; 912a842acaSChristoph Hellwig } 92566c0d6aSRoman Peniaev 9383096ebfSTejun Heo if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > 9483096ebfSTejun Heo get_capacity(req->rq_disk)) 952a842acaSChristoph Hellwig return BLK_STS_IOERR; 961da177e4SLinus Torvalds 97aebf526bSChristoph Hellwig switch (req_op(req)) { 98aebf526bSChristoph Hellwig case REQ_OP_DISCARD: 992a842acaSChristoph Hellwig if (tr->discard(dev, block, nsect)) 1002a842acaSChristoph Hellwig return BLK_STS_IOERR; 1012a842acaSChristoph Hellwig return BLK_STS_OK; 102aebf526bSChristoph Hellwig case REQ_OP_READ: 10319187672SRichard Purdie for (; nsect > 0; nsect--, block++, buf += tr->blksize) 1041da177e4SLinus Torvalds if (tr->readsect(dev, block, buf)) 1052a842acaSChristoph Hellwig return BLK_STS_IOERR; 1062d4dc890SIlya Loginov rq_flush_dcache_pages(req); 1072a842acaSChristoph Hellwig return BLK_STS_OK; 108aebf526bSChristoph Hellwig case REQ_OP_WRITE: 1091da177e4SLinus Torvalds if (!tr->writesect) 1102a842acaSChristoph Hellwig return BLK_STS_IOERR; 1111da177e4SLinus Torvalds 1122d4dc890SIlya Loginov rq_flush_dcache_pages(req); 11319187672SRichard Purdie for (; nsect > 0; nsect--, block++, buf += tr->blksize) 1141da177e4SLinus Torvalds if (tr->writesect(dev, block, buf)) 1152a842acaSChristoph Hellwig return BLK_STS_IOERR; 116*9a515447SAbhishek Sahu return BLK_STS_OK; 117aebf526bSChristoph Hellwig default: 1182a842acaSChristoph Hellwig return BLK_STS_IOERR; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 122c7519dbfSJarkko Lavinen int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev) 123c7519dbfSJarkko Lavinen { 1247bf7e370SArtem Bityutskiy return dev->bg_stop; 125c7519dbfSJarkko Lavinen } 126c7519dbfSJarkko Lavinen EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background); 127c7519dbfSJarkko Lavinen 12822a8578fSEzequiel Garcia static void mtd_blktrans_work(struct work_struct *work) 1291da177e4SLinus Torvalds { 13022a8578fSEzequiel Garcia struct mtd_blktrans_dev *dev = 13122a8578fSEzequiel Garcia container_of(work, struct mtd_blktrans_dev, work); 132c7519dbfSJarkko Lavinen struct mtd_blktrans_ops *tr = dev->tr; 133a8638622SMaxim Levitsky struct request_queue *rq = dev->rq; 1341498ada7STejun Heo struct request *req = NULL; 135c7519dbfSJarkko Lavinen int background_done = 0; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds spin_lock_irq(rq->queue_lock); 1381498ada7STejun Heo 13922a8578fSEzequiel Garcia while (1) { 1402a842acaSChristoph Hellwig blk_status_t res; 1411da177e4SLinus Torvalds 1427bf7e370SArtem Bityutskiy dev->bg_stop = false; 1439934c8c0STejun Heo if (!req && !(req = blk_fetch_request(rq))) { 144c7519dbfSJarkko Lavinen if (tr->background && !background_done) { 145c7519dbfSJarkko Lavinen spin_unlock_irq(rq->queue_lock); 146c7519dbfSJarkko Lavinen mutex_lock(&dev->lock); 147c7519dbfSJarkko Lavinen tr->background(dev); 148c7519dbfSJarkko Lavinen mutex_unlock(&dev->lock); 149c7519dbfSJarkko Lavinen spin_lock_irq(rq->queue_lock); 150c7519dbfSJarkko Lavinen /* 151c7519dbfSJarkko Lavinen * Do background processing just once per idle 152c7519dbfSJarkko Lavinen * period. 153c7519dbfSJarkko Lavinen */ 1547bf7e370SArtem Bityutskiy background_done = !dev->bg_stop; 155c7519dbfSJarkko Lavinen continue; 156c7519dbfSJarkko Lavinen } 15722a8578fSEzequiel Garcia break; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds spin_unlock_irq(rq->queue_lock); 1611da177e4SLinus Torvalds 16248b19268SIngo Molnar mutex_lock(&dev->lock); 163a8638622SMaxim Levitsky res = do_blktrans_request(dev->tr, dev, req); 16448b19268SIngo Molnar mutex_unlock(&dev->lock); 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds spin_lock_irq(rq->queue_lock); 1671da177e4SLinus Torvalds 1681498ada7STejun Heo if (!__blk_end_request_cur(req, res)) 1691498ada7STejun Heo req = NULL; 170c7519dbfSJarkko Lavinen 171c7519dbfSJarkko Lavinen background_done = 0; 1721da177e4SLinus Torvalds } 1731498ada7STejun Heo 1741da177e4SLinus Torvalds spin_unlock_irq(rq->queue_lock); 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds static void mtd_blktrans_request(struct request_queue *rq) 1781da177e4SLinus Torvalds { 179048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev; 180048d8719SMaxim Levitsky struct request *req = NULL; 181048d8719SMaxim Levitsky 182048d8719SMaxim Levitsky dev = rq->queuedata; 183048d8719SMaxim Levitsky 184048d8719SMaxim Levitsky if (!dev) 185048d8719SMaxim Levitsky while ((req = blk_fetch_request(rq)) != NULL) 1862a842acaSChristoph Hellwig __blk_end_request_all(req, BLK_STS_IOERR); 18722a8578fSEzequiel Garcia else 18822a8578fSEzequiel Garcia queue_work(dev->wq, &dev->work); 1897bf7e370SArtem Bityutskiy } 1901da177e4SLinus Torvalds 191af0e2a0aSAl Viro static int blktrans_open(struct block_device *bdev, fmode_t mode) 1921da177e4SLinus Torvalds { 193048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 194008c751eSMaxim Levitsky int ret = 0; 1951da177e4SLinus Torvalds 196048d8719SMaxim Levitsky if (!dev) 1976e9624b8SArnd Bergmann return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ 1981da177e4SLinus Torvalds 199073db4a5SBrian Norris mutex_lock(&mtd_table_mutex); 200f3c63795SBrian Norris mutex_lock(&dev->lock); 2011da177e4SLinus Torvalds 202342ff28fSBrian Norris if (dev->open) 203048d8719SMaxim Levitsky goto unlock; 204008c751eSMaxim Levitsky 205008c751eSMaxim Levitsky kref_get(&dev->ref); 206008c751eSMaxim Levitsky __module_get(dev->tr->owner); 207008c751eSMaxim Levitsky 20894735ec4SArtem Bityutskiy if (!dev->mtd) 20994735ec4SArtem Bityutskiy goto unlock; 21094735ec4SArtem Bityutskiy 21194735ec4SArtem Bityutskiy if (dev->tr->open) { 21294735ec4SArtem Bityutskiy ret = dev->tr->open(dev); 21394735ec4SArtem Bityutskiy if (ret) 21494735ec4SArtem Bityutskiy goto error_put; 2151da177e4SLinus Torvalds } 216048d8719SMaxim Levitsky 21794735ec4SArtem Bityutskiy ret = __get_mtd_device(dev->mtd); 21894735ec4SArtem Bityutskiy if (ret) 21994735ec4SArtem Bityutskiy goto error_release; 22070d5098aSAlexander Stein dev->file_mode = mode; 22194735ec4SArtem Bityutskiy 222048d8719SMaxim Levitsky unlock: 223342ff28fSBrian Norris dev->open++; 224048d8719SMaxim Levitsky mutex_unlock(&dev->lock); 225f3c63795SBrian Norris mutex_unlock(&mtd_table_mutex); 226048d8719SMaxim Levitsky blktrans_dev_put(dev); 2271da177e4SLinus Torvalds return ret; 22894735ec4SArtem Bityutskiy 22994735ec4SArtem Bityutskiy error_release: 23094735ec4SArtem Bityutskiy if (dev->tr->release) 23194735ec4SArtem Bityutskiy dev->tr->release(dev); 23294735ec4SArtem Bityutskiy error_put: 23394735ec4SArtem Bityutskiy module_put(dev->tr->owner); 23494735ec4SArtem Bityutskiy kref_put(&dev->ref, blktrans_dev_release); 23594735ec4SArtem Bityutskiy mutex_unlock(&dev->lock); 236f3c63795SBrian Norris mutex_unlock(&mtd_table_mutex); 23794735ec4SArtem Bityutskiy blktrans_dev_put(dev); 23894735ec4SArtem Bityutskiy return ret; 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 241db2a144bSAl Viro static void blktrans_release(struct gendisk *disk, fmode_t mode) 2421da177e4SLinus Torvalds { 243048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); 2441da177e4SLinus Torvalds 245048d8719SMaxim Levitsky if (!dev) 246db2a144bSAl Viro return; 2471da177e4SLinus Torvalds 248073db4a5SBrian Norris mutex_lock(&mtd_table_mutex); 249f3c63795SBrian Norris mutex_lock(&dev->lock); 2501da177e4SLinus Torvalds 251008c751eSMaxim Levitsky if (--dev->open) 252048d8719SMaxim Levitsky goto unlock; 253048d8719SMaxim Levitsky 254008c751eSMaxim Levitsky kref_put(&dev->ref, blktrans_dev_release); 255008c751eSMaxim Levitsky module_put(dev->tr->owner); 256008c751eSMaxim Levitsky 257008c751eSMaxim Levitsky if (dev->mtd) { 258a8ca889eSAl Viro if (dev->tr->release) 259a8ca889eSAl Viro dev->tr->release(dev); 260008c751eSMaxim Levitsky __put_mtd_device(dev->mtd); 261008c751eSMaxim Levitsky } 262048d8719SMaxim Levitsky unlock: 263048d8719SMaxim Levitsky mutex_unlock(&dev->lock); 264f3c63795SBrian Norris mutex_unlock(&mtd_table_mutex); 265048d8719SMaxim Levitsky blktrans_dev_put(dev); 2661da177e4SLinus Torvalds } 2671da177e4SLinus Torvalds 268a885c8c4SChristoph Hellwig static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) 269a885c8c4SChristoph Hellwig { 270048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 271048d8719SMaxim Levitsky int ret = -ENXIO; 272a885c8c4SChristoph Hellwig 273048d8719SMaxim Levitsky if (!dev) 274048d8719SMaxim Levitsky return ret; 275048d8719SMaxim Levitsky 276048d8719SMaxim Levitsky mutex_lock(&dev->lock); 277048d8719SMaxim Levitsky 278048d8719SMaxim Levitsky if (!dev->mtd) 279048d8719SMaxim Levitsky goto unlock; 280048d8719SMaxim Levitsky 281c4a3f13cSBrian Norris ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY; 282048d8719SMaxim Levitsky unlock: 283048d8719SMaxim Levitsky mutex_unlock(&dev->lock); 284048d8719SMaxim Levitsky blktrans_dev_put(dev); 285048d8719SMaxim Levitsky return ret; 286a885c8c4SChristoph Hellwig } 2871da177e4SLinus Torvalds 288af0e2a0aSAl Viro static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, 2891da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 2901da177e4SLinus Torvalds { 291048d8719SMaxim Levitsky struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 292048d8719SMaxim Levitsky int ret = -ENXIO; 293048d8719SMaxim Levitsky 294048d8719SMaxim Levitsky if (!dev) 295048d8719SMaxim Levitsky return ret; 296048d8719SMaxim Levitsky 297048d8719SMaxim Levitsky mutex_lock(&dev->lock); 298048d8719SMaxim Levitsky 299048d8719SMaxim Levitsky if (!dev->mtd) 300048d8719SMaxim Levitsky goto unlock; 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds switch (cmd) { 3031da177e4SLinus Torvalds case BLKFLSBUF: 304048d8719SMaxim Levitsky ret = dev->tr->flush ? dev->tr->flush(dev) : 0; 305007c2d87SDan Carpenter break; 3061da177e4SLinus Torvalds default: 307048d8719SMaxim Levitsky ret = -ENOTTY; 3081da177e4SLinus Torvalds } 309048d8719SMaxim Levitsky unlock: 310048d8719SMaxim Levitsky mutex_unlock(&dev->lock); 311048d8719SMaxim Levitsky blktrans_dev_put(dev); 312048d8719SMaxim Levitsky return ret; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3159329c5ebSEzequiel Garcia static const struct block_device_operations mtd_block_ops = { 3161da177e4SLinus Torvalds .owner = THIS_MODULE, 317af0e2a0aSAl Viro .open = blktrans_open, 318af0e2a0aSAl Viro .release = blktrans_release, 3198a6cfeb6SArnd Bergmann .ioctl = blktrans_ioctl, 320a885c8c4SChristoph Hellwig .getgeo = blktrans_getgeo, 3211da177e4SLinus Torvalds }; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) 3241da177e4SLinus Torvalds { 3251da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = new->tr; 32671a928c0SChris Malley struct mtd_blktrans_dev *d; 3271da177e4SLinus Torvalds int last_devnum = -1; 3281da177e4SLinus Torvalds struct gendisk *gd; 329a8638622SMaxim Levitsky int ret; 3301da177e4SLinus Torvalds 331b3561ea9SJean Delvare if (mutex_trylock(&mtd_table_mutex)) { 33248b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 3331da177e4SLinus Torvalds BUG(); 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 336048d8719SMaxim Levitsky mutex_lock(&blktrans_ref_mutex); 33771a928c0SChris Malley list_for_each_entry(d, &tr->devs, list) { 3381da177e4SLinus Torvalds if (new->devnum == -1) { 3391da177e4SLinus Torvalds /* Use first free number */ 3401da177e4SLinus Torvalds if (d->devnum != last_devnum+1) { 3411da177e4SLinus Torvalds /* Found a free devnum. Plug it in here */ 3421da177e4SLinus Torvalds new->devnum = last_devnum+1; 3431da177e4SLinus Torvalds list_add_tail(&new->list, &d->list); 3441da177e4SLinus Torvalds goto added; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds } else if (d->devnum == new->devnum) { 3471da177e4SLinus Torvalds /* Required number taken */ 348048d8719SMaxim Levitsky mutex_unlock(&blktrans_ref_mutex); 3491da177e4SLinus Torvalds return -EBUSY; 3501da177e4SLinus Torvalds } else if (d->devnum > new->devnum) { 3511da177e4SLinus Torvalds /* Required number was free */ 3521da177e4SLinus Torvalds list_add_tail(&new->list, &d->list); 3531da177e4SLinus Torvalds goto added; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds last_devnum = d->devnum; 3561da177e4SLinus Torvalds } 357a8638622SMaxim Levitsky 358a8638622SMaxim Levitsky ret = -EBUSY; 3591da177e4SLinus Torvalds if (new->devnum == -1) 3601da177e4SLinus Torvalds new->devnum = last_devnum+1; 3611da177e4SLinus Torvalds 3624d3a8534SBen Hutchings /* Check that the device and any partitions will get valid 3634d3a8534SBen Hutchings * minor numbers and that the disk naming code below can cope 3644d3a8534SBen Hutchings * with this number. */ 3654d3a8534SBen Hutchings if (new->devnum > (MINORMASK >> tr->part_bits) || 366048d8719SMaxim Levitsky (tr->part_bits && new->devnum >= 27 * 26)) { 367048d8719SMaxim Levitsky mutex_unlock(&blktrans_ref_mutex); 368a8638622SMaxim Levitsky goto error1; 369048d8719SMaxim Levitsky } 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds list_add_tail(&new->list, &tr->devs); 3721da177e4SLinus Torvalds added: 373048d8719SMaxim Levitsky mutex_unlock(&blktrans_ref_mutex); 374048d8719SMaxim Levitsky 375ce37ab42SDavid Woodhouse mutex_init(&new->lock); 376048d8719SMaxim Levitsky kref_init(&new->ref); 3771da177e4SLinus Torvalds if (!tr->writesect) 3781da177e4SLinus Torvalds new->readonly = 1; 3791da177e4SLinus Torvalds 380a8638622SMaxim Levitsky /* Create gendisk */ 381a8638622SMaxim Levitsky ret = -ENOMEM; 3821da177e4SLinus Torvalds gd = alloc_disk(1 << tr->part_bits); 383a8638622SMaxim Levitsky 384a8638622SMaxim Levitsky if (!gd) 385a8638622SMaxim Levitsky goto error2; 386a8638622SMaxim Levitsky 387a8638622SMaxim Levitsky new->disk = gd; 388a8638622SMaxim Levitsky gd->private_data = new; 3891da177e4SLinus Torvalds gd->major = tr->major; 3901da177e4SLinus Torvalds gd->first_minor = (new->devnum) << tr->part_bits; 3919329c5ebSEzequiel Garcia gd->fops = &mtd_block_ops; 3921da177e4SLinus Torvalds 39365a8de36STodd Poynor if (tr->part_bits) 39465a8de36STodd Poynor if (new->devnum < 26) 3951da177e4SLinus Torvalds snprintf(gd->disk_name, sizeof(gd->disk_name), 39665a8de36STodd Poynor "%s%c", tr->name, 'a' + new->devnum); 39765a8de36STodd Poynor else 39865a8de36STodd Poynor snprintf(gd->disk_name, sizeof(gd->disk_name), 39965a8de36STodd Poynor "%s%c%c", tr->name, 40065a8de36STodd Poynor 'a' - 1 + new->devnum / 26, 40165a8de36STodd Poynor 'a' + new->devnum % 26); 40265a8de36STodd Poynor else 40365a8de36STodd Poynor snprintf(gd->disk_name, sizeof(gd->disk_name), 40465a8de36STodd Poynor "%s%d", tr->name, new->devnum); 4051da177e4SLinus Torvalds 4062ce401d5SPeng Fan set_capacity(gd, ((u64)new->size * tr->blksize) >> 9); 4071da177e4SLinus Torvalds 408a8638622SMaxim Levitsky /* Create the request queue */ 409a8638622SMaxim Levitsky spin_lock_init(&new->queue_lock); 410a8638622SMaxim Levitsky new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); 411a8638622SMaxim Levitsky 412a8638622SMaxim Levitsky if (!new->rq) 413a8638622SMaxim Levitsky goto error3; 414a8638622SMaxim Levitsky 415566c0d6aSRoman Peniaev if (tr->flush) 416fec3ff5dSJens Axboe blk_queue_write_cache(new->rq, true, false); 417566c0d6aSRoman Peniaev 418a8638622SMaxim Levitsky new->rq->queuedata = new; 419a8638622SMaxim Levitsky blk_queue_logical_block_size(new->rq, tr->blksize); 420a8638622SMaxim Levitsky 4218fc45044SChristoph Hellwig blk_queue_bounce_limit(new->rq, BLK_BOUNCE_HIGH); 42216f7eca5SDan McGee queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq); 423b277da0aSMike Snitzer queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, new->rq); 42416f7eca5SDan McGee 425115ee88cSJarkko Lavinen if (tr->discard) { 426115ee88cSJarkko Lavinen queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq); 4272bb4cd5cSJens Axboe blk_queue_max_discard_sectors(new->rq, UINT_MAX); 428115ee88cSJarkko Lavinen } 429a8638622SMaxim Levitsky 430a8638622SMaxim Levitsky gd->queue = new->rq; 431a8638622SMaxim Levitsky 43222a8578fSEzequiel Garcia /* Create processing workqueue */ 43322a8578fSEzequiel Garcia new->wq = alloc_workqueue("%s%d", 0, 0, 43422a8578fSEzequiel Garcia tr->name, new->mtd->index); 43522a8578fSEzequiel Garcia if (!new->wq) 436a8638622SMaxim Levitsky goto error4; 43722a8578fSEzequiel Garcia INIT_WORK(&new->work, mtd_blktrans_work); 43822a8578fSEzequiel Garcia 4391da177e4SLinus Torvalds if (new->readonly) 4401da177e4SLinus Torvalds set_disk_ro(gd, 1); 4411da177e4SLinus Torvalds 4420d52c756SDan Williams device_add_disk(&new->mtd->dev, gd); 443026ec578SMaxim Levitsky 444133fa8c7SMaxim Levitsky if (new->disk_attributes) { 445133fa8c7SMaxim Levitsky ret = sysfs_create_group(&disk_to_dev(gd)->kobj, 446026ec578SMaxim Levitsky new->disk_attributes); 447133fa8c7SMaxim Levitsky WARN_ON(ret); 448133fa8c7SMaxim Levitsky } 4491da177e4SLinus Torvalds return 0; 450a8638622SMaxim Levitsky error4: 451a8638622SMaxim Levitsky blk_cleanup_queue(new->rq); 452a8638622SMaxim Levitsky error3: 453a8638622SMaxim Levitsky put_disk(new->disk); 454a8638622SMaxim Levitsky error2: 455a8638622SMaxim Levitsky list_del(&new->list); 456a8638622SMaxim Levitsky error1: 457a8638622SMaxim Levitsky return ret; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) 4611da177e4SLinus Torvalds { 462048d8719SMaxim Levitsky unsigned long flags; 463048d8719SMaxim Levitsky 464b3561ea9SJean Delvare if (mutex_trylock(&mtd_table_mutex)) { 46548b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 4661da177e4SLinus Torvalds BUG(); 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds 469026ec578SMaxim Levitsky if (old->disk_attributes) 470026ec578SMaxim Levitsky sysfs_remove_group(&disk_to_dev(old->disk)->kobj, 471026ec578SMaxim Levitsky old->disk_attributes); 472026ec578SMaxim Levitsky 473dba76c03SMaxim Levitsky /* Stop new requests to arrive */ 474dba76c03SMaxim Levitsky del_gendisk(old->disk); 475dba76c03SMaxim Levitsky 47622a8578fSEzequiel Garcia /* Stop workqueue. This will perform any pending request. */ 47722a8578fSEzequiel Garcia destroy_workqueue(old->wq); 478a8638622SMaxim Levitsky 479048d8719SMaxim Levitsky /* Kill current requests */ 480048d8719SMaxim Levitsky spin_lock_irqsave(&old->queue_lock, flags); 481048d8719SMaxim Levitsky old->rq->queuedata = NULL; 482048d8719SMaxim Levitsky blk_start_queue(old->rq); 483048d8719SMaxim Levitsky spin_unlock_irqrestore(&old->queue_lock, flags); 484048d8719SMaxim Levitsky 485008c751eSMaxim Levitsky /* If the device is currently open, tell trans driver to close it, 486008c751eSMaxim Levitsky then put mtd device, and don't touch it again */ 487048d8719SMaxim Levitsky mutex_lock(&old->lock); 488008c751eSMaxim Levitsky if (old->open) { 489008c751eSMaxim Levitsky if (old->tr->release) 490048d8719SMaxim Levitsky old->tr->release(old); 491008c751eSMaxim Levitsky __put_mtd_device(old->mtd); 492048d8719SMaxim Levitsky } 493048d8719SMaxim Levitsky 494048d8719SMaxim Levitsky old->mtd = NULL; 495048d8719SMaxim Levitsky 496048d8719SMaxim Levitsky mutex_unlock(&old->lock); 497048d8719SMaxim Levitsky blktrans_dev_put(old); 4981da177e4SLinus Torvalds return 0; 4991da177e4SLinus Torvalds } 5001da177e4SLinus Torvalds 5011da177e4SLinus Torvalds static void blktrans_notify_remove(struct mtd_info *mtd) 5021da177e4SLinus Torvalds { 50371a928c0SChris Malley struct mtd_blktrans_ops *tr; 50471a928c0SChris Malley struct mtd_blktrans_dev *dev, *next; 5051da177e4SLinus Torvalds 50671a928c0SChris Malley list_for_each_entry(tr, &blktrans_majors, list) 50771a928c0SChris Malley list_for_each_entry_safe(dev, next, &tr->devs, list) 5081da177e4SLinus Torvalds if (dev->mtd == mtd) 5091da177e4SLinus Torvalds tr->remove_dev(dev); 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds static void blktrans_notify_add(struct mtd_info *mtd) 5131da177e4SLinus Torvalds { 51471a928c0SChris Malley struct mtd_blktrans_ops *tr; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds if (mtd->type == MTD_ABSENT) 5171da177e4SLinus Torvalds return; 5181da177e4SLinus Torvalds 51971a928c0SChris Malley list_for_each_entry(tr, &blktrans_majors, list) 5201da177e4SLinus Torvalds tr->add_mtd(tr, mtd); 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds static struct mtd_notifier blktrans_notifier = { 5241da177e4SLinus Torvalds .add = blktrans_notify_add, 5251da177e4SLinus Torvalds .remove = blktrans_notify_remove, 5261da177e4SLinus Torvalds }; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds int register_mtd_blktrans(struct mtd_blktrans_ops *tr) 5291da177e4SLinus Torvalds { 530f1332ba2SBen Hutchings struct mtd_info *mtd; 531f1332ba2SBen Hutchings int ret; 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /* Register the notifier if/when the first device type is 5341da177e4SLinus Torvalds registered, to prevent the link/init ordering from fucking 5351da177e4SLinus Torvalds us over. */ 5361da177e4SLinus Torvalds if (!blktrans_notifier.list.next) 5371da177e4SLinus Torvalds register_mtd_user(&blktrans_notifier); 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds 54048b19268SIngo Molnar mutex_lock(&mtd_table_mutex); 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds ret = register_blkdev(tr->major, tr->name); 5436fe4c590SFrank Li if (ret < 0) { 5441da177e4SLinus Torvalds printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", 5451da177e4SLinus Torvalds tr->name, tr->major, ret); 54648b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 5471da177e4SLinus Torvalds return ret; 5481da177e4SLinus Torvalds } 549eae9acd1SDavid Woodhouse 5506fe4c590SFrank Li if (ret) 5516fe4c590SFrank Li tr->major = ret; 5526fe4c590SFrank Li 55319187672SRichard Purdie tr->blkshift = ffs(tr->blksize) - 1; 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds INIT_LIST_HEAD(&tr->devs); 5561da177e4SLinus Torvalds list_add(&tr->list, &blktrans_majors); 5571da177e4SLinus Torvalds 558f1332ba2SBen Hutchings mtd_for_each_device(mtd) 559f1332ba2SBen Hutchings if (mtd->type != MTD_ABSENT) 560f1332ba2SBen Hutchings tr->add_mtd(tr, mtd); 5611da177e4SLinus Torvalds 56248b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 5631da177e4SLinus Torvalds return 0; 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) 5671da177e4SLinus Torvalds { 56871a928c0SChris Malley struct mtd_blktrans_dev *dev, *next; 5691da177e4SLinus Torvalds 57048b19268SIngo Molnar mutex_lock(&mtd_table_mutex); 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds /* Remove it from the list of active majors */ 5731da177e4SLinus Torvalds list_del(&tr->list); 5741da177e4SLinus Torvalds 57571a928c0SChris Malley list_for_each_entry_safe(dev, next, &tr->devs, list) 5761da177e4SLinus Torvalds tr->remove_dev(dev); 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds unregister_blkdev(tr->major, tr->name); 57948b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 5801da177e4SLinus Torvalds 581373ebfbfSEric Sesterhenn BUG_ON(!list_empty(&tr->devs)); 5821da177e4SLinus Torvalds return 0; 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds static void __exit mtd_blktrans_exit(void) 5861da177e4SLinus Torvalds { 5871da177e4SLinus Torvalds /* No race here -- if someone's currently in register_mtd_blktrans 5881da177e4SLinus Torvalds we're screwed anyway. */ 5891da177e4SLinus Torvalds if (blktrans_notifier.list.next) 5901da177e4SLinus Torvalds unregister_mtd_user(&blktrans_notifier); 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds module_exit(mtd_blktrans_exit); 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(register_mtd_blktrans); 5961da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); 5971da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); 5981da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 6011da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 6021da177e4SLinus Torvalds MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); 603