11da177e4SLinus Torvalds /* 297894cdaSThomas Gleixner * $Id: mtd_blkdevs.c,v 1.27 2005/11/07 11:14:20 gleixner Exp $ 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * (C) 2003 David Woodhouse <dwmw2@infradead.org> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Interface to Linux 2.5 block layer for MTD 'translation layers'. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include <linux/kernel.h> 111da177e4SLinus Torvalds #include <linux/slab.h> 121da177e4SLinus Torvalds #include <linux/module.h> 131da177e4SLinus Torvalds #include <linux/list.h> 141da177e4SLinus Torvalds #include <linux/fs.h> 151da177e4SLinus Torvalds #include <linux/mtd/blktrans.h> 161da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 171da177e4SLinus Torvalds #include <linux/blkdev.h> 181da177e4SLinus Torvalds #include <linux/blkpg.h> 191da177e4SLinus Torvalds #include <linux/spinlock.h> 201da177e4SLinus Torvalds #include <linux/hdreg.h> 211da177e4SLinus Torvalds #include <linux/init.h> 2248b19268SIngo Molnar #include <linux/mutex.h> 231da177e4SLinus Torvalds #include <asm/uaccess.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds static LIST_HEAD(blktrans_majors); 261da177e4SLinus Torvalds 2748b19268SIngo Molnar extern struct mutex mtd_table_mutex; 281da177e4SLinus Torvalds extern struct mtd_info *mtd_table[]; 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds struct mtd_blkcore_priv { 311da177e4SLinus Torvalds struct completion thread_dead; 321da177e4SLinus Torvalds int exiting; 331da177e4SLinus Torvalds wait_queue_head_t thread_wq; 341da177e4SLinus Torvalds struct request_queue *rq; 351da177e4SLinus Torvalds spinlock_t queue_lock; 361da177e4SLinus Torvalds }; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds static int do_blktrans_request(struct mtd_blktrans_ops *tr, 391da177e4SLinus Torvalds struct mtd_blktrans_dev *dev, 401da177e4SLinus Torvalds struct request *req) 411da177e4SLinus Torvalds { 421da177e4SLinus Torvalds unsigned long block, nsect; 431da177e4SLinus Torvalds char *buf; 441da177e4SLinus Torvalds 45*19187672SRichard Purdie block = req->sector << 9 >> tr->blkshift; 46*19187672SRichard Purdie nsect = req->current_nr_sectors << 9 >> tr->blkshift; 47*19187672SRichard Purdie 481da177e4SLinus Torvalds buf = req->buffer; 491da177e4SLinus Torvalds 504aff5e23SJens Axboe if (!blk_fs_request(req)) 511da177e4SLinus Torvalds return 0; 521da177e4SLinus Torvalds 53*19187672SRichard Purdie if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk)) 541da177e4SLinus Torvalds return 0; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds switch(rq_data_dir(req)) { 571da177e4SLinus Torvalds case READ: 58*19187672SRichard Purdie for (; nsect > 0; nsect--, block++, buf += tr->blksize) 591da177e4SLinus Torvalds if (tr->readsect(dev, block, buf)) 601da177e4SLinus Torvalds return 0; 611da177e4SLinus Torvalds return 1; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds case WRITE: 641da177e4SLinus Torvalds if (!tr->writesect) 651da177e4SLinus Torvalds return 0; 661da177e4SLinus Torvalds 67*19187672SRichard Purdie for (; nsect > 0; nsect--, block++, buf += tr->blksize) 681da177e4SLinus Torvalds if (tr->writesect(dev, block, buf)) 691da177e4SLinus Torvalds return 0; 701da177e4SLinus Torvalds return 1; 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds default: 739a292308SJeff Garzik printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); 741da177e4SLinus Torvalds return 0; 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static int mtd_blktrans_thread(void *arg) 791da177e4SLinus Torvalds { 801da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = arg; 811da177e4SLinus Torvalds struct request_queue *rq = tr->blkcore_priv->rq; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds /* we might get involved when memory gets low, so use PF_MEMALLOC */ 841da177e4SLinus Torvalds current->flags |= PF_MEMALLOC | PF_NOFREEZE; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds daemonize("%sd", tr->name); 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* daemonize() doesn't do this for us since some kernel threads 891da177e4SLinus Torvalds actually want to deal with signals. We can't just call 901da177e4SLinus Torvalds exit_sighand() since that'll cause an oops when we finally 911da177e4SLinus Torvalds do exit. */ 921da177e4SLinus Torvalds spin_lock_irq(¤t->sighand->siglock); 931da177e4SLinus Torvalds sigfillset(¤t->blocked); 941da177e4SLinus Torvalds recalc_sigpending(); 951da177e4SLinus Torvalds spin_unlock_irq(¤t->sighand->siglock); 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds spin_lock_irq(rq->queue_lock); 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds while (!tr->blkcore_priv->exiting) { 1001da177e4SLinus Torvalds struct request *req; 1011da177e4SLinus Torvalds struct mtd_blktrans_dev *dev; 1021da177e4SLinus Torvalds int res = 0; 1031da177e4SLinus Torvalds DECLARE_WAITQUEUE(wait, current); 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds req = elv_next_request(rq); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds if (!req) { 1081da177e4SLinus Torvalds add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); 1091da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds spin_unlock_irq(rq->queue_lock); 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds schedule(); 1141da177e4SLinus Torvalds remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds spin_lock_irq(rq->queue_lock); 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds continue; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds dev = req->rq_disk->private_data; 1221da177e4SLinus Torvalds tr = dev->tr; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds spin_unlock_irq(rq->queue_lock); 1251da177e4SLinus Torvalds 12648b19268SIngo Molnar mutex_lock(&dev->lock); 1271da177e4SLinus Torvalds res = do_blktrans_request(tr, dev, req); 12848b19268SIngo Molnar mutex_unlock(&dev->lock); 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds spin_lock_irq(rq->queue_lock); 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds end_request(req, res); 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds spin_unlock_irq(rq->queue_lock); 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds complete_and_exit(&tr->blkcore_priv->thread_dead, 0); 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds static void mtd_blktrans_request(struct request_queue *rq) 1401da177e4SLinus Torvalds { 1411da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = rq->queuedata; 1421da177e4SLinus Torvalds wake_up(&tr->blkcore_priv->thread_wq); 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static int blktrans_open(struct inode *i, struct file *f) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds struct mtd_blktrans_dev *dev; 1491da177e4SLinus Torvalds struct mtd_blktrans_ops *tr; 1501da177e4SLinus Torvalds int ret = -ENODEV; 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds dev = i->i_bdev->bd_disk->private_data; 1531da177e4SLinus Torvalds tr = dev->tr; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds if (!try_module_get(dev->mtd->owner)) 1561da177e4SLinus Torvalds goto out; 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds if (!try_module_get(tr->owner)) 1591da177e4SLinus Torvalds goto out_tr; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* FIXME: Locking. A hot pluggable device can go away 1621da177e4SLinus Torvalds (del_mtd_device can be called for it) without its module 1631da177e4SLinus Torvalds being unloaded. */ 1641da177e4SLinus Torvalds dev->mtd->usecount++; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds ret = 0; 1671da177e4SLinus Torvalds if (tr->open && (ret = tr->open(dev))) { 1681da177e4SLinus Torvalds dev->mtd->usecount--; 1691da177e4SLinus Torvalds module_put(dev->mtd->owner); 1701da177e4SLinus Torvalds out_tr: 1711da177e4SLinus Torvalds module_put(tr->owner); 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds out: 1741da177e4SLinus Torvalds return ret; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds static int blktrans_release(struct inode *i, struct file *f) 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds struct mtd_blktrans_dev *dev; 1801da177e4SLinus Torvalds struct mtd_blktrans_ops *tr; 1811da177e4SLinus Torvalds int ret = 0; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds dev = i->i_bdev->bd_disk->private_data; 1841da177e4SLinus Torvalds tr = dev->tr; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds if (tr->release) 1871da177e4SLinus Torvalds ret = tr->release(dev); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds if (!ret) { 1901da177e4SLinus Torvalds dev->mtd->usecount--; 1911da177e4SLinus Torvalds module_put(dev->mtd->owner); 1921da177e4SLinus Torvalds module_put(tr->owner); 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds return ret; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 198a885c8c4SChristoph Hellwig static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) 199a885c8c4SChristoph Hellwig { 200a885c8c4SChristoph Hellwig struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; 201a885c8c4SChristoph Hellwig 202a885c8c4SChristoph Hellwig if (dev->tr->getgeo) 203a885c8c4SChristoph Hellwig return dev->tr->getgeo(dev, geo); 204a885c8c4SChristoph Hellwig return -ENOTTY; 205a885c8c4SChristoph Hellwig } 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds static int blktrans_ioctl(struct inode *inode, struct file *file, 2081da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; 2111da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = dev->tr; 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds switch (cmd) { 2141da177e4SLinus Torvalds case BLKFLSBUF: 2151da177e4SLinus Torvalds if (tr->flush) 2161da177e4SLinus Torvalds return tr->flush(dev); 2171da177e4SLinus Torvalds /* The core code did the work, we had nothing to do. */ 2181da177e4SLinus Torvalds return 0; 2191da177e4SLinus Torvalds default: 2201da177e4SLinus Torvalds return -ENOTTY; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds struct block_device_operations mtd_blktrans_ops = { 2251da177e4SLinus Torvalds .owner = THIS_MODULE, 2261da177e4SLinus Torvalds .open = blktrans_open, 2271da177e4SLinus Torvalds .release = blktrans_release, 2281da177e4SLinus Torvalds .ioctl = blktrans_ioctl, 229a885c8c4SChristoph Hellwig .getgeo = blktrans_getgeo, 2301da177e4SLinus Torvalds }; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) 2331da177e4SLinus Torvalds { 2341da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = new->tr; 2351da177e4SLinus Torvalds struct list_head *this; 2361da177e4SLinus Torvalds int last_devnum = -1; 2371da177e4SLinus Torvalds struct gendisk *gd; 2381da177e4SLinus Torvalds 23948b19268SIngo Molnar if (!!mutex_trylock(&mtd_table_mutex)) { 24048b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 2411da177e4SLinus Torvalds BUG(); 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds list_for_each(this, &tr->devs) { 2451da177e4SLinus Torvalds struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); 2461da177e4SLinus Torvalds if (new->devnum == -1) { 2471da177e4SLinus Torvalds /* Use first free number */ 2481da177e4SLinus Torvalds if (d->devnum != last_devnum+1) { 2491da177e4SLinus Torvalds /* Found a free devnum. Plug it in here */ 2501da177e4SLinus Torvalds new->devnum = last_devnum+1; 2511da177e4SLinus Torvalds list_add_tail(&new->list, &d->list); 2521da177e4SLinus Torvalds goto added; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds } else if (d->devnum == new->devnum) { 2551da177e4SLinus Torvalds /* Required number taken */ 2561da177e4SLinus Torvalds return -EBUSY; 2571da177e4SLinus Torvalds } else if (d->devnum > new->devnum) { 2581da177e4SLinus Torvalds /* Required number was free */ 2591da177e4SLinus Torvalds list_add_tail(&new->list, &d->list); 2601da177e4SLinus Torvalds goto added; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds last_devnum = d->devnum; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds if (new->devnum == -1) 2651da177e4SLinus Torvalds new->devnum = last_devnum+1; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds if ((new->devnum << tr->part_bits) > 256) { 2681da177e4SLinus Torvalds return -EBUSY; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 27148b19268SIngo Molnar mutex_init(&new->lock); 2721da177e4SLinus Torvalds list_add_tail(&new->list, &tr->devs); 2731da177e4SLinus Torvalds added: 2741da177e4SLinus Torvalds if (!tr->writesect) 2751da177e4SLinus Torvalds new->readonly = 1; 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds gd = alloc_disk(1 << tr->part_bits); 2781da177e4SLinus Torvalds if (!gd) { 2791da177e4SLinus Torvalds list_del(&new->list); 2801da177e4SLinus Torvalds return -ENOMEM; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds gd->major = tr->major; 2831da177e4SLinus Torvalds gd->first_minor = (new->devnum) << tr->part_bits; 2841da177e4SLinus Torvalds gd->fops = &mtd_blktrans_ops; 2851da177e4SLinus Torvalds 28665a8de36STodd Poynor if (tr->part_bits) 28765a8de36STodd Poynor if (new->devnum < 26) 2881da177e4SLinus Torvalds snprintf(gd->disk_name, sizeof(gd->disk_name), 28965a8de36STodd Poynor "%s%c", tr->name, 'a' + new->devnum); 29065a8de36STodd Poynor else 29165a8de36STodd Poynor snprintf(gd->disk_name, sizeof(gd->disk_name), 29265a8de36STodd Poynor "%s%c%c", tr->name, 29365a8de36STodd Poynor 'a' - 1 + new->devnum / 26, 29465a8de36STodd Poynor 'a' + new->devnum % 26); 29565a8de36STodd Poynor else 29665a8de36STodd Poynor snprintf(gd->disk_name, sizeof(gd->disk_name), 29765a8de36STodd Poynor "%s%d", tr->name, new->devnum); 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds /* 2.5 has capacity in units of 512 bytes while still 3001da177e4SLinus Torvalds having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ 301*19187672SRichard Purdie set_capacity(gd, (new->size * tr->blksize) >> 9); 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds gd->private_data = new; 3041da177e4SLinus Torvalds new->blkcore_priv = gd; 3051da177e4SLinus Torvalds gd->queue = tr->blkcore_priv->rq; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds if (new->readonly) 3081da177e4SLinus Torvalds set_disk_ro(gd, 1); 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds add_disk(gd); 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds return 0; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) 3161da177e4SLinus Torvalds { 31748b19268SIngo Molnar if (!!mutex_trylock(&mtd_table_mutex)) { 31848b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 3191da177e4SLinus Torvalds BUG(); 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds list_del(&old->list); 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds del_gendisk(old->blkcore_priv); 3251da177e4SLinus Torvalds put_disk(old->blkcore_priv); 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds return 0; 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds static void blktrans_notify_remove(struct mtd_info *mtd) 3311da177e4SLinus Torvalds { 3321da177e4SLinus Torvalds struct list_head *this, *this2, *next; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds list_for_each(this, &blktrans_majors) { 3351da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds list_for_each_safe(this2, next, &tr->devs) { 3381da177e4SLinus Torvalds struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds if (dev->mtd == mtd) 3411da177e4SLinus Torvalds tr->remove_dev(dev); 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds static void blktrans_notify_add(struct mtd_info *mtd) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds struct list_head *this; 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds if (mtd->type == MTD_ABSENT) 3511da177e4SLinus Torvalds return; 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds list_for_each(this, &blktrans_majors) { 3541da177e4SLinus Torvalds struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds tr->add_mtd(tr, mtd); 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds static struct mtd_notifier blktrans_notifier = { 3621da177e4SLinus Torvalds .add = blktrans_notify_add, 3631da177e4SLinus Torvalds .remove = blktrans_notify_remove, 3641da177e4SLinus Torvalds }; 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds int register_mtd_blktrans(struct mtd_blktrans_ops *tr) 3671da177e4SLinus Torvalds { 3681da177e4SLinus Torvalds int ret, i; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds /* Register the notifier if/when the first device type is 3711da177e4SLinus Torvalds registered, to prevent the link/init ordering from fucking 3721da177e4SLinus Torvalds us over. */ 3731da177e4SLinus Torvalds if (!blktrans_notifier.list.next) 3741da177e4SLinus Torvalds register_mtd_user(&blktrans_notifier); 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); 3771da177e4SLinus Torvalds if (!tr->blkcore_priv) 3781da177e4SLinus Torvalds return -ENOMEM; 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); 3811da177e4SLinus Torvalds 38248b19268SIngo Molnar mutex_lock(&mtd_table_mutex); 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds ret = register_blkdev(tr->major, tr->name); 3851da177e4SLinus Torvalds if (ret) { 3861da177e4SLinus Torvalds printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", 3871da177e4SLinus Torvalds tr->name, tr->major, ret); 3881da177e4SLinus Torvalds kfree(tr->blkcore_priv); 38948b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 3901da177e4SLinus Torvalds return ret; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds spin_lock_init(&tr->blkcore_priv->queue_lock); 3931da177e4SLinus Torvalds init_completion(&tr->blkcore_priv->thread_dead); 3941da177e4SLinus Torvalds init_waitqueue_head(&tr->blkcore_priv->thread_wq); 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); 3971da177e4SLinus Torvalds if (!tr->blkcore_priv->rq) { 3981da177e4SLinus Torvalds unregister_blkdev(tr->major, tr->name); 3991da177e4SLinus Torvalds kfree(tr->blkcore_priv); 40048b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 4011da177e4SLinus Torvalds return -ENOMEM; 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds 4041da177e4SLinus Torvalds tr->blkcore_priv->rq->queuedata = tr; 405*19187672SRichard Purdie blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); 406*19187672SRichard Purdie tr->blkshift = ffs(tr->blksize) - 1; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL); 4091da177e4SLinus Torvalds if (ret < 0) { 4101da177e4SLinus Torvalds blk_cleanup_queue(tr->blkcore_priv->rq); 4111da177e4SLinus Torvalds unregister_blkdev(tr->major, tr->name); 4121da177e4SLinus Torvalds kfree(tr->blkcore_priv); 41348b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 4141da177e4SLinus Torvalds return ret; 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds INIT_LIST_HEAD(&tr->devs); 4181da177e4SLinus Torvalds list_add(&tr->list, &blktrans_majors); 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds for (i=0; i<MAX_MTD_DEVICES; i++) { 4211da177e4SLinus Torvalds if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) 4221da177e4SLinus Torvalds tr->add_mtd(tr, mtd_table[i]); 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 42548b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds return 0; 4281da177e4SLinus Torvalds } 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) 4311da177e4SLinus Torvalds { 4321da177e4SLinus Torvalds struct list_head *this, *next; 4331da177e4SLinus Torvalds 43448b19268SIngo Molnar mutex_lock(&mtd_table_mutex); 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds /* Clean up the kernel thread */ 4371da177e4SLinus Torvalds tr->blkcore_priv->exiting = 1; 4381da177e4SLinus Torvalds wake_up(&tr->blkcore_priv->thread_wq); 4391da177e4SLinus Torvalds wait_for_completion(&tr->blkcore_priv->thread_dead); 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* Remove it from the list of active majors */ 4421da177e4SLinus Torvalds list_del(&tr->list); 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds list_for_each_safe(this, next, &tr->devs) { 4451da177e4SLinus Torvalds struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); 4461da177e4SLinus Torvalds tr->remove_dev(dev); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds blk_cleanup_queue(tr->blkcore_priv->rq); 4501da177e4SLinus Torvalds unregister_blkdev(tr->major, tr->name); 4511da177e4SLinus Torvalds 45248b19268SIngo Molnar mutex_unlock(&mtd_table_mutex); 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds kfree(tr->blkcore_priv); 4551da177e4SLinus Torvalds 456373ebfbfSEric Sesterhenn BUG_ON(!list_empty(&tr->devs)); 4571da177e4SLinus Torvalds return 0; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds static void __exit mtd_blktrans_exit(void) 4611da177e4SLinus Torvalds { 4621da177e4SLinus Torvalds /* No race here -- if someone's currently in register_mtd_blktrans 4631da177e4SLinus Torvalds we're screwed anyway. */ 4641da177e4SLinus Torvalds if (blktrans_notifier.list.next) 4651da177e4SLinus Torvalds unregister_mtd_user(&blktrans_notifier); 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds 4681da177e4SLinus Torvalds module_exit(mtd_blktrans_exit); 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(register_mtd_blktrans); 4711da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); 4721da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); 4731da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 4761da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 4771da177e4SLinus Torvalds MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); 478