18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Interface to Linux block layer for MTD 'translation layers'. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/fs.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/blktrans.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 158c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 168c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 178c2ecf20Sopenharmony_ci#include <linux/blkpg.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "mtdcore.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic LIST_HEAD(blktrans_majors); 268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(blktrans_ref_mutex); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void blktrans_dev_release(struct kref *kref) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev = 318c2ecf20Sopenharmony_ci container_of(kref, struct mtd_blktrans_dev, ref); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci dev->disk->private_data = NULL; 348c2ecf20Sopenharmony_ci blk_cleanup_queue(dev->rq); 358c2ecf20Sopenharmony_ci blk_mq_free_tag_set(dev->tag_set); 368c2ecf20Sopenharmony_ci kfree(dev->tag_set); 378c2ecf20Sopenharmony_ci put_disk(dev->disk); 388c2ecf20Sopenharmony_ci list_del(&dev->list); 398c2ecf20Sopenharmony_ci kfree(dev); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci mutex_lock(&blktrans_ref_mutex); 478c2ecf20Sopenharmony_ci dev = disk->private_data; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!dev) 508c2ecf20Sopenharmony_ci goto unlock; 518c2ecf20Sopenharmony_ci kref_get(&dev->ref); 528c2ecf20Sopenharmony_ciunlock: 538c2ecf20Sopenharmony_ci mutex_unlock(&blktrans_ref_mutex); 548c2ecf20Sopenharmony_ci return dev; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void blktrans_dev_put(struct mtd_blktrans_dev *dev) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci mutex_lock(&blktrans_ref_mutex); 608c2ecf20Sopenharmony_ci kref_put(&dev->ref, blktrans_dev_release); 618c2ecf20Sopenharmony_ci mutex_unlock(&blktrans_ref_mutex); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic blk_status_t do_blktrans_request(struct mtd_blktrans_ops *tr, 668c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev, 678c2ecf20Sopenharmony_ci struct request *req) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci unsigned long block, nsect; 708c2ecf20Sopenharmony_ci char *buf; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci block = blk_rq_pos(req) << 9 >> tr->blkshift; 738c2ecf20Sopenharmony_ci nsect = blk_rq_cur_bytes(req) >> tr->blkshift; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (req_op(req) == REQ_OP_FLUSH) { 768c2ecf20Sopenharmony_ci if (tr->flush(dev)) 778c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 788c2ecf20Sopenharmony_ci return BLK_STS_OK; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > 828c2ecf20Sopenharmony_ci get_capacity(req->rq_disk)) 838c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci switch (req_op(req)) { 868c2ecf20Sopenharmony_ci case REQ_OP_DISCARD: 878c2ecf20Sopenharmony_ci if (tr->discard(dev, block, nsect)) 888c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 898c2ecf20Sopenharmony_ci return BLK_STS_OK; 908c2ecf20Sopenharmony_ci case REQ_OP_READ: 918c2ecf20Sopenharmony_ci buf = kmap(bio_page(req->bio)) + bio_offset(req->bio); 928c2ecf20Sopenharmony_ci for (; nsect > 0; nsect--, block++, buf += tr->blksize) { 938c2ecf20Sopenharmony_ci if (tr->readsect(dev, block, buf)) { 948c2ecf20Sopenharmony_ci kunmap(bio_page(req->bio)); 958c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci kunmap(bio_page(req->bio)); 998c2ecf20Sopenharmony_ci rq_flush_dcache_pages(req); 1008c2ecf20Sopenharmony_ci return BLK_STS_OK; 1018c2ecf20Sopenharmony_ci case REQ_OP_WRITE: 1028c2ecf20Sopenharmony_ci if (!tr->writesect) 1038c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci rq_flush_dcache_pages(req); 1068c2ecf20Sopenharmony_ci buf = kmap(bio_page(req->bio)) + bio_offset(req->bio); 1078c2ecf20Sopenharmony_ci for (; nsect > 0; nsect--, block++, buf += tr->blksize) { 1088c2ecf20Sopenharmony_ci if (tr->writesect(dev, block, buf)) { 1098c2ecf20Sopenharmony_ci kunmap(bio_page(req->bio)); 1108c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci kunmap(bio_page(req->bio)); 1148c2ecf20Sopenharmony_ci return BLK_STS_OK; 1158c2ecf20Sopenharmony_ci default: 1168c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return dev->bg_stop; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtd_blktrans_cease_background); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct request *mtd_next_request(struct mtd_blktrans_dev *dev) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct request *rq; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rq = list_first_entry_or_null(&dev->rq_list, struct request, queuelist); 1318c2ecf20Sopenharmony_ci if (rq) { 1328c2ecf20Sopenharmony_ci list_del_init(&rq->queuelist); 1338c2ecf20Sopenharmony_ci blk_mq_start_request(rq); 1348c2ecf20Sopenharmony_ci return rq; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return NULL; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void mtd_blktrans_work(struct mtd_blktrans_dev *dev) 1418c2ecf20Sopenharmony_ci __releases(&dev->queue_lock) 1428c2ecf20Sopenharmony_ci __acquires(&dev->queue_lock) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct mtd_blktrans_ops *tr = dev->tr; 1458c2ecf20Sopenharmony_ci struct request *req = NULL; 1468c2ecf20Sopenharmony_ci int background_done = 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci while (1) { 1498c2ecf20Sopenharmony_ci blk_status_t res; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci dev->bg_stop = false; 1528c2ecf20Sopenharmony_ci if (!req && !(req = mtd_next_request(dev))) { 1538c2ecf20Sopenharmony_ci if (tr->background && !background_done) { 1548c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->queue_lock); 1558c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1568c2ecf20Sopenharmony_ci tr->background(dev); 1578c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1588c2ecf20Sopenharmony_ci spin_lock_irq(&dev->queue_lock); 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Do background processing just once per idle 1618c2ecf20Sopenharmony_ci * period. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci background_done = !dev->bg_stop; 1648c2ecf20Sopenharmony_ci continue; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->queue_lock); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1728c2ecf20Sopenharmony_ci res = do_blktrans_request(dev->tr, dev, req); 1738c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!blk_update_request(req, res, blk_rq_cur_bytes(req))) { 1768c2ecf20Sopenharmony_ci __blk_mq_end_request(req, res); 1778c2ecf20Sopenharmony_ci req = NULL; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci background_done = 0; 1818c2ecf20Sopenharmony_ci spin_lock_irq(&dev->queue_lock); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic blk_status_t mtd_queue_rq(struct blk_mq_hw_ctx *hctx, 1868c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci dev = hctx->queue->queuedata; 1918c2ecf20Sopenharmony_ci if (!dev) { 1928c2ecf20Sopenharmony_ci blk_mq_start_request(bd->rq); 1938c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci spin_lock_irq(&dev->queue_lock); 1978c2ecf20Sopenharmony_ci list_add_tail(&bd->rq->queuelist, &dev->rq_list); 1988c2ecf20Sopenharmony_ci mtd_blktrans_work(dev); 1998c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->queue_lock); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return BLK_STS_OK; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int blktrans_open(struct block_device *bdev, fmode_t mode) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 2078c2ecf20Sopenharmony_ci int ret = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!dev) 2108c2ecf20Sopenharmony_ci return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci mtd_table_mutex_lock(); 2138c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (dev->open) 2168c2ecf20Sopenharmony_ci goto unlock; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci kref_get(&dev->ref); 2198c2ecf20Sopenharmony_ci __module_get(dev->tr->owner); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!dev->mtd) 2228c2ecf20Sopenharmony_ci goto unlock; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (dev->tr->open) { 2258c2ecf20Sopenharmony_ci ret = dev->tr->open(dev); 2268c2ecf20Sopenharmony_ci if (ret) 2278c2ecf20Sopenharmony_ci goto error_put; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = __get_mtd_device(dev->mtd); 2318c2ecf20Sopenharmony_ci if (ret) 2328c2ecf20Sopenharmony_ci goto error_release; 2338c2ecf20Sopenharmony_ci dev->file_mode = mode; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ciunlock: 2368c2ecf20Sopenharmony_ci dev->open++; 2378c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2388c2ecf20Sopenharmony_ci mtd_table_mutex_unlock(); 2398c2ecf20Sopenharmony_ci blktrans_dev_put(dev); 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cierror_release: 2438c2ecf20Sopenharmony_ci if (dev->tr->release) 2448c2ecf20Sopenharmony_ci dev->tr->release(dev); 2458c2ecf20Sopenharmony_cierror_put: 2468c2ecf20Sopenharmony_ci module_put(dev->tr->owner); 2478c2ecf20Sopenharmony_ci kref_put(&dev->ref, blktrans_dev_release); 2488c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2498c2ecf20Sopenharmony_ci mtd_table_mutex_unlock(); 2508c2ecf20Sopenharmony_ci blktrans_dev_put(dev); 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void blktrans_release(struct gendisk *disk, fmode_t mode) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!dev) 2598c2ecf20Sopenharmony_ci return; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mtd_table_mutex_lock(); 2628c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (--dev->open) 2658c2ecf20Sopenharmony_ci goto unlock; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci kref_put(&dev->ref, blktrans_dev_release); 2688c2ecf20Sopenharmony_ci module_put(dev->tr->owner); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (dev->mtd) { 2718c2ecf20Sopenharmony_ci if (dev->tr->release) 2728c2ecf20Sopenharmony_ci dev->tr->release(dev); 2738c2ecf20Sopenharmony_ci __put_mtd_device(dev->mtd); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ciunlock: 2768c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2778c2ecf20Sopenharmony_ci mtd_table_mutex_unlock(); 2788c2ecf20Sopenharmony_ci blktrans_dev_put(dev); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 2848c2ecf20Sopenharmony_ci int ret = -ENXIO; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!dev) 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!dev->mtd) 2928c2ecf20Sopenharmony_ci goto unlock; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY; 2958c2ecf20Sopenharmony_ciunlock: 2968c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2978c2ecf20Sopenharmony_ci blktrans_dev_put(dev); 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int blktrans_ioctl(struct block_device *bdev, fmode_t mode, 3028c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); 3058c2ecf20Sopenharmony_ci int ret = -ENXIO; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!dev) 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (!dev->mtd) 3138c2ecf20Sopenharmony_ci goto unlock; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci switch (cmd) { 3168c2ecf20Sopenharmony_ci case BLKFLSBUF: 3178c2ecf20Sopenharmony_ci ret = dev->tr->flush ? dev->tr->flush(dev) : 0; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci default: 3208c2ecf20Sopenharmony_ci ret = -ENOTTY; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ciunlock: 3238c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 3248c2ecf20Sopenharmony_ci blktrans_dev_put(dev); 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic const struct block_device_operations mtd_block_ops = { 3298c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3308c2ecf20Sopenharmony_ci .open = blktrans_open, 3318c2ecf20Sopenharmony_ci .release = blktrans_release, 3328c2ecf20Sopenharmony_ci .ioctl = blktrans_ioctl, 3338c2ecf20Sopenharmony_ci .getgeo = blktrans_getgeo, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic const struct blk_mq_ops mtd_mq_ops = { 3378c2ecf20Sopenharmony_ci .queue_rq = mtd_queue_rq, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciint add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct mtd_blktrans_ops *tr = new->tr; 3438c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *d; 3448c2ecf20Sopenharmony_ci int last_devnum = -1; 3458c2ecf20Sopenharmony_ci struct gendisk *gd; 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci mtd_table_assert_mutex_locked(); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&blktrans_ref_mutex); 3518c2ecf20Sopenharmony_ci list_for_each_entry(d, &tr->devs, list) { 3528c2ecf20Sopenharmony_ci if (new->devnum == -1) { 3538c2ecf20Sopenharmony_ci /* Use first free number */ 3548c2ecf20Sopenharmony_ci if (d->devnum != last_devnum+1) { 3558c2ecf20Sopenharmony_ci /* Found a free devnum. Plug it in here */ 3568c2ecf20Sopenharmony_ci new->devnum = last_devnum+1; 3578c2ecf20Sopenharmony_ci list_add_tail(&new->list, &d->list); 3588c2ecf20Sopenharmony_ci goto added; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci } else if (d->devnum == new->devnum) { 3618c2ecf20Sopenharmony_ci /* Required number taken */ 3628c2ecf20Sopenharmony_ci mutex_unlock(&blktrans_ref_mutex); 3638c2ecf20Sopenharmony_ci return -EBUSY; 3648c2ecf20Sopenharmony_ci } else if (d->devnum > new->devnum) { 3658c2ecf20Sopenharmony_ci /* Required number was free */ 3668c2ecf20Sopenharmony_ci list_add_tail(&new->list, &d->list); 3678c2ecf20Sopenharmony_ci goto added; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci last_devnum = d->devnum; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = -EBUSY; 3738c2ecf20Sopenharmony_ci if (new->devnum == -1) 3748c2ecf20Sopenharmony_ci new->devnum = last_devnum+1; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Check that the device and any partitions will get valid 3778c2ecf20Sopenharmony_ci * minor numbers and that the disk naming code below can cope 3788c2ecf20Sopenharmony_ci * with this number. */ 3798c2ecf20Sopenharmony_ci if (new->devnum > (MINORMASK >> tr->part_bits) || 3808c2ecf20Sopenharmony_ci (tr->part_bits && new->devnum >= 27 * 26)) { 3818c2ecf20Sopenharmony_ci mutex_unlock(&blktrans_ref_mutex); 3828c2ecf20Sopenharmony_ci goto error1; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci list_add_tail(&new->list, &tr->devs); 3868c2ecf20Sopenharmony_ci added: 3878c2ecf20Sopenharmony_ci mutex_unlock(&blktrans_ref_mutex); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci mutex_init(&new->lock); 3908c2ecf20Sopenharmony_ci kref_init(&new->ref); 3918c2ecf20Sopenharmony_ci if (!tr->writesect) 3928c2ecf20Sopenharmony_ci new->readonly = 1; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Create gendisk */ 3958c2ecf20Sopenharmony_ci ret = -ENOMEM; 3968c2ecf20Sopenharmony_ci gd = alloc_disk(1 << tr->part_bits); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (!gd) 3998c2ecf20Sopenharmony_ci goto error2; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci new->disk = gd; 4028c2ecf20Sopenharmony_ci gd->private_data = new; 4038c2ecf20Sopenharmony_ci gd->major = tr->major; 4048c2ecf20Sopenharmony_ci gd->first_minor = (new->devnum) << tr->part_bits; 4058c2ecf20Sopenharmony_ci gd->fops = &mtd_block_ops; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (tr->part_bits) 4088c2ecf20Sopenharmony_ci if (new->devnum < 26) 4098c2ecf20Sopenharmony_ci snprintf(gd->disk_name, sizeof(gd->disk_name), 4108c2ecf20Sopenharmony_ci "%s%c", tr->name, 'a' + new->devnum); 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci snprintf(gd->disk_name, sizeof(gd->disk_name), 4138c2ecf20Sopenharmony_ci "%s%c%c", tr->name, 4148c2ecf20Sopenharmony_ci 'a' - 1 + new->devnum / 26, 4158c2ecf20Sopenharmony_ci 'a' + new->devnum % 26); 4168c2ecf20Sopenharmony_ci else 4178c2ecf20Sopenharmony_ci snprintf(gd->disk_name, sizeof(gd->disk_name), 4188c2ecf20Sopenharmony_ci "%s%d", tr->name, new->devnum); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci set_capacity(gd, ((u64)new->size * tr->blksize) >> 9); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Create the request queue */ 4238c2ecf20Sopenharmony_ci spin_lock_init(&new->queue_lock); 4248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new->rq_list); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci new->tag_set = kzalloc(sizeof(*new->tag_set), GFP_KERNEL); 4278c2ecf20Sopenharmony_ci if (!new->tag_set) 4288c2ecf20Sopenharmony_ci goto error3; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci new->rq = blk_mq_init_sq_queue(new->tag_set, &mtd_mq_ops, 2, 4318c2ecf20Sopenharmony_ci BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING); 4328c2ecf20Sopenharmony_ci if (IS_ERR(new->rq)) { 4338c2ecf20Sopenharmony_ci ret = PTR_ERR(new->rq); 4348c2ecf20Sopenharmony_ci new->rq = NULL; 4358c2ecf20Sopenharmony_ci goto error4; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (tr->flush) 4398c2ecf20Sopenharmony_ci blk_queue_write_cache(new->rq, true, false); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci new->rq->queuedata = new; 4428c2ecf20Sopenharmony_ci blk_queue_logical_block_size(new->rq, tr->blksize); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, new->rq); 4458c2ecf20Sopenharmony_ci blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, new->rq); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (tr->discard) { 4488c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_DISCARD, new->rq); 4498c2ecf20Sopenharmony_ci blk_queue_max_discard_sectors(new->rq, UINT_MAX); 4508c2ecf20Sopenharmony_ci new->rq->limits.discard_granularity = tr->blksize; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci gd->queue = new->rq; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (new->readonly) 4568c2ecf20Sopenharmony_ci set_disk_ro(gd, 1); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci device_add_disk(&new->mtd->dev, gd, NULL); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (new->disk_attributes) { 4618c2ecf20Sopenharmony_ci ret = sysfs_create_group(&disk_to_dev(gd)->kobj, 4628c2ecf20Sopenharmony_ci new->disk_attributes); 4638c2ecf20Sopenharmony_ci WARN_ON(ret); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_cierror4: 4678c2ecf20Sopenharmony_ci kfree(new->tag_set); 4688c2ecf20Sopenharmony_cierror3: 4698c2ecf20Sopenharmony_ci put_disk(new->disk); 4708c2ecf20Sopenharmony_cierror2: 4718c2ecf20Sopenharmony_ci list_del(&new->list); 4728c2ecf20Sopenharmony_cierror1: 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciint del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci unsigned long flags; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci mtd_table_assert_mutex_locked(); 4818c2ecf20Sopenharmony_ci if (old->disk_attributes) 4828c2ecf20Sopenharmony_ci sysfs_remove_group(&disk_to_dev(old->disk)->kobj, 4838c2ecf20Sopenharmony_ci old->disk_attributes); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Stop new requests to arrive */ 4868c2ecf20Sopenharmony_ci del_gendisk(old->disk); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* Kill current requests */ 4898c2ecf20Sopenharmony_ci spin_lock_irqsave(&old->queue_lock, flags); 4908c2ecf20Sopenharmony_ci old->rq->queuedata = NULL; 4918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&old->queue_lock, flags); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* freeze+quiesce queue to ensure all requests are flushed */ 4948c2ecf20Sopenharmony_ci blk_mq_freeze_queue(old->rq); 4958c2ecf20Sopenharmony_ci blk_mq_quiesce_queue(old->rq); 4968c2ecf20Sopenharmony_ci blk_mq_unquiesce_queue(old->rq); 4978c2ecf20Sopenharmony_ci blk_mq_unfreeze_queue(old->rq); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* If the device is currently open, tell trans driver to close it, 5008c2ecf20Sopenharmony_ci then put mtd device, and don't touch it again */ 5018c2ecf20Sopenharmony_ci mutex_lock(&old->lock); 5028c2ecf20Sopenharmony_ci if (old->open) { 5038c2ecf20Sopenharmony_ci if (old->tr->release) 5048c2ecf20Sopenharmony_ci old->tr->release(old); 5058c2ecf20Sopenharmony_ci __put_mtd_device(old->mtd); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci old->mtd = NULL; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci mutex_unlock(&old->lock); 5118c2ecf20Sopenharmony_ci blktrans_dev_put(old); 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void blktrans_notify_remove(struct mtd_info *mtd) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct mtd_blktrans_ops *tr; 5188c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev, *next; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci list_for_each_entry(tr, &blktrans_majors, list) 5218c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, next, &tr->devs, list) 5228c2ecf20Sopenharmony_ci if (dev->mtd == mtd) 5238c2ecf20Sopenharmony_ci tr->remove_dev(dev); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void blktrans_notify_add(struct mtd_info *mtd) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct mtd_blktrans_ops *tr; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (mtd->type == MTD_ABSENT || mtd->type == MTD_UBIVOLUME) 5318c2ecf20Sopenharmony_ci return; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci list_for_each_entry(tr, &blktrans_majors, list) 5348c2ecf20Sopenharmony_ci tr->add_mtd(tr, mtd); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic struct mtd_notifier blktrans_notifier = { 5388c2ecf20Sopenharmony_ci .add = blktrans_notify_add, 5398c2ecf20Sopenharmony_ci .remove = blktrans_notify_remove, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciint register_mtd_blktrans(struct mtd_blktrans_ops *tr) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct mtd_info *mtd; 5458c2ecf20Sopenharmony_ci int ret; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Register the notifier if/when the first device type is 5488c2ecf20Sopenharmony_ci registered, to prevent the link/init ordering from fucking 5498c2ecf20Sopenharmony_ci us over. */ 5508c2ecf20Sopenharmony_ci if (!blktrans_notifier.list.next) 5518c2ecf20Sopenharmony_ci register_mtd_user(&blktrans_notifier); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci mtd_table_mutex_lock(); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ret = register_blkdev(tr->major, tr->name); 5578c2ecf20Sopenharmony_ci if (ret < 0) { 5588c2ecf20Sopenharmony_ci printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", 5598c2ecf20Sopenharmony_ci tr->name, tr->major, ret); 5608c2ecf20Sopenharmony_ci mtd_table_mutex_unlock(); 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (ret) 5658c2ecf20Sopenharmony_ci tr->major = ret; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci tr->blkshift = ffs(tr->blksize) - 1; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tr->devs); 5708c2ecf20Sopenharmony_ci list_add(&tr->list, &blktrans_majors); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci mtd_for_each_device(mtd) 5738c2ecf20Sopenharmony_ci if (mtd->type != MTD_ABSENT && mtd->type != MTD_UBIVOLUME) 5748c2ecf20Sopenharmony_ci tr->add_mtd(tr, mtd); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci mtd_table_mutex_unlock(); 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ciint deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *dev, *next; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci mtd_table_mutex_lock(); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Remove it from the list of active majors */ 5878c2ecf20Sopenharmony_ci list_del(&tr->list); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, next, &tr->devs, list) 5908c2ecf20Sopenharmony_ci tr->remove_dev(dev); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci unregister_blkdev(tr->major, tr->name); 5938c2ecf20Sopenharmony_ci mtd_table_mutex_unlock(); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&tr->devs)); 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic void __exit mtd_blktrans_exit(void) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci /* No race here -- if someone's currently in register_mtd_blktrans 6028c2ecf20Sopenharmony_ci we're screwed anyway. */ 6038c2ecf20Sopenharmony_ci if (blktrans_notifier.list.next) 6048c2ecf20Sopenharmony_ci unregister_mtd_user(&blktrans_notifier); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cimodule_exit(mtd_blktrans_exit); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_mtd_blktrans); 6108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(deregister_mtd_blktrans); 6118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); 6128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 6158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); 617