150acfb2bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29d54c8a3SEzequiel Garcia /* 39d54c8a3SEzequiel Garcia * Copyright (c) 2014 Ezequiel Garcia 49d54c8a3SEzequiel Garcia * Copyright (c) 2011 Free Electrons 59d54c8a3SEzequiel Garcia * 69d54c8a3SEzequiel Garcia * Driver parameter handling strongly based on drivers/mtd/ubi/build.c 79d54c8a3SEzequiel Garcia * Copyright (c) International Business Machines Corp., 2006 89d54c8a3SEzequiel Garcia * Copyright (c) Nokia Corporation, 2007 99d54c8a3SEzequiel Garcia * Authors: Artem Bityutskiy, Frank Haverkamp 109d54c8a3SEzequiel Garcia */ 119d54c8a3SEzequiel Garcia 129d54c8a3SEzequiel Garcia /* 139d54c8a3SEzequiel Garcia * Read-only block devices on top of UBI volumes 149d54c8a3SEzequiel Garcia * 159d54c8a3SEzequiel Garcia * A simple implementation to allow a block device to be layered on top of a 169d54c8a3SEzequiel Garcia * UBI volume. The implementation is provided by creating a static 1-to-1 179d54c8a3SEzequiel Garcia * mapping between the block device and the UBI volume. 189d54c8a3SEzequiel Garcia * 199d54c8a3SEzequiel Garcia * The addressed byte is obtained from the addressed block sector, which is 209d54c8a3SEzequiel Garcia * mapped linearly into the corresponding LEB: 219d54c8a3SEzequiel Garcia * 229d54c8a3SEzequiel Garcia * LEB number = addressed byte / LEB size 239d54c8a3SEzequiel Garcia * 244d283ee2SArtem Bityutskiy * This feature is compiled in the UBI core, and adds a 'block' parameter 254d283ee2SArtem Bityutskiy * to allow early creation of block devices on top of UBI volumes. Runtime 264d283ee2SArtem Bityutskiy * block creation/removal for UBI volumes is provided through two UBI ioctls: 278af87188SArtem Bityutskiy * UBI_IOCVOLCRBLK and UBI_IOCVOLRMBLK. 289d54c8a3SEzequiel Garcia */ 299d54c8a3SEzequiel Garcia 309d54c8a3SEzequiel Garcia #include <linux/module.h> 319d54c8a3SEzequiel Garcia #include <linux/init.h> 329d54c8a3SEzequiel Garcia #include <linux/err.h> 339d54c8a3SEzequiel Garcia #include <linux/kernel.h> 349d54c8a3SEzequiel Garcia #include <linux/list.h> 359d54c8a3SEzequiel Garcia #include <linux/mutex.h> 369d54c8a3SEzequiel Garcia #include <linux/slab.h> 379d54c8a3SEzequiel Garcia #include <linux/mtd/ubi.h> 389d54c8a3SEzequiel Garcia #include <linux/workqueue.h> 399d54c8a3SEzequiel Garcia #include <linux/blkdev.h> 40ff1f48eeSRichard Weinberger #include <linux/blk-mq.h> 419d54c8a3SEzequiel Garcia #include <linux/hdreg.h> 42ff1f48eeSRichard Weinberger #include <linux/scatterlist.h> 432bf50d42SDan Ehrenberg #include <linux/idr.h> 449d54c8a3SEzequiel Garcia #include <asm/div64.h> 459d54c8a3SEzequiel Garcia 469d54c8a3SEzequiel Garcia #include "ubi-media.h" 479d54c8a3SEzequiel Garcia #include "ubi.h" 489d54c8a3SEzequiel Garcia 499d54c8a3SEzequiel Garcia /* Maximum number of supported devices */ 509d54c8a3SEzequiel Garcia #define UBIBLOCK_MAX_DEVICES 32 519d54c8a3SEzequiel Garcia 529d54c8a3SEzequiel Garcia /* Maximum length of the 'block=' parameter */ 539d54c8a3SEzequiel Garcia #define UBIBLOCK_PARAM_LEN 63 549d54c8a3SEzequiel Garcia 559d54c8a3SEzequiel Garcia /* Maximum number of comma-separated items in the 'block=' parameter */ 569d54c8a3SEzequiel Garcia #define UBIBLOCK_PARAM_COUNT 2 579d54c8a3SEzequiel Garcia 589d54c8a3SEzequiel Garcia struct ubiblock_param { 599d54c8a3SEzequiel Garcia int ubi_num; 609d54c8a3SEzequiel Garcia int vol_id; 619d54c8a3SEzequiel Garcia char name[UBIBLOCK_PARAM_LEN+1]; 629d54c8a3SEzequiel Garcia }; 639d54c8a3SEzequiel Garcia 64ff1f48eeSRichard Weinberger struct ubiblock_pdu { 65ff1f48eeSRichard Weinberger struct work_struct work; 66ff1f48eeSRichard Weinberger struct ubi_sgl usgl; 67ff1f48eeSRichard Weinberger }; 68ff1f48eeSRichard Weinberger 699d54c8a3SEzequiel Garcia /* Numbers of elements set in the @ubiblock_param array */ 709d54c8a3SEzequiel Garcia static int ubiblock_devs __initdata; 719d54c8a3SEzequiel Garcia 729d54c8a3SEzequiel Garcia /* MTD devices specification parameters */ 739d54c8a3SEzequiel Garcia static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; 749d54c8a3SEzequiel Garcia 759d54c8a3SEzequiel Garcia struct ubiblock { 769d54c8a3SEzequiel Garcia struct ubi_volume_desc *desc; 779d54c8a3SEzequiel Garcia int ubi_num; 789d54c8a3SEzequiel Garcia int vol_id; 799d54c8a3SEzequiel Garcia int refcnt; 809d54c8a3SEzequiel Garcia int leb_size; 819d54c8a3SEzequiel Garcia 829d54c8a3SEzequiel Garcia struct gendisk *gd; 839d54c8a3SEzequiel Garcia struct request_queue *rq; 849d54c8a3SEzequiel Garcia 859d54c8a3SEzequiel Garcia struct workqueue_struct *wq; 869d54c8a3SEzequiel Garcia 879d54c8a3SEzequiel Garcia struct mutex dev_mutex; 889d54c8a3SEzequiel Garcia struct list_head list; 89ff1f48eeSRichard Weinberger struct blk_mq_tag_set tag_set; 909d54c8a3SEzequiel Garcia }; 919d54c8a3SEzequiel Garcia 929d54c8a3SEzequiel Garcia /* Linked list of all ubiblock instances */ 939d54c8a3SEzequiel Garcia static LIST_HEAD(ubiblock_devices); 947f29ae9fSBradley Bolen static DEFINE_IDR(ubiblock_minor_idr); 957f29ae9fSBradley Bolen /* Protects ubiblock_devices and ubiblock_minor_idr */ 969d54c8a3SEzequiel Garcia static DEFINE_MUTEX(devices_mutex); 979d54c8a3SEzequiel Garcia static int ubiblock_major; 989d54c8a3SEzequiel Garcia 999d54c8a3SEzequiel Garcia static int __init ubiblock_set_param(const char *val, 1009d54c8a3SEzequiel Garcia const struct kernel_param *kp) 1019d54c8a3SEzequiel Garcia { 1029d54c8a3SEzequiel Garcia int i, ret; 1039d54c8a3SEzequiel Garcia size_t len; 1049d54c8a3SEzequiel Garcia struct ubiblock_param *param; 1059d54c8a3SEzequiel Garcia char buf[UBIBLOCK_PARAM_LEN]; 1069d54c8a3SEzequiel Garcia char *pbuf = &buf[0]; 1079d54c8a3SEzequiel Garcia char *tokens[UBIBLOCK_PARAM_COUNT]; 1089d54c8a3SEzequiel Garcia 1099d54c8a3SEzequiel Garcia if (!val) 1109d54c8a3SEzequiel Garcia return -EINVAL; 1119d54c8a3SEzequiel Garcia 1129d54c8a3SEzequiel Garcia len = strnlen(val, UBIBLOCK_PARAM_LEN); 1139d54c8a3SEzequiel Garcia if (len == 0) { 11432608703STanya Brokhman pr_warn("UBI: block: empty 'block=' parameter - ignored\n"); 1159d54c8a3SEzequiel Garcia return 0; 1169d54c8a3SEzequiel Garcia } 1179d54c8a3SEzequiel Garcia 1189d54c8a3SEzequiel Garcia if (len == UBIBLOCK_PARAM_LEN) { 11932608703STanya Brokhman pr_err("UBI: block: parameter \"%s\" is too long, max. is %d\n", 1209d54c8a3SEzequiel Garcia val, UBIBLOCK_PARAM_LEN); 1219d54c8a3SEzequiel Garcia return -EINVAL; 1229d54c8a3SEzequiel Garcia } 1239d54c8a3SEzequiel Garcia 1249d54c8a3SEzequiel Garcia strcpy(buf, val); 1259d54c8a3SEzequiel Garcia 1269d54c8a3SEzequiel Garcia /* Get rid of the final newline */ 1279d54c8a3SEzequiel Garcia if (buf[len - 1] == '\n') 1289d54c8a3SEzequiel Garcia buf[len - 1] = '\0'; 1299d54c8a3SEzequiel Garcia 1309d54c8a3SEzequiel Garcia for (i = 0; i < UBIBLOCK_PARAM_COUNT; i++) 1319d54c8a3SEzequiel Garcia tokens[i] = strsep(&pbuf, ","); 1329d54c8a3SEzequiel Garcia 1339d54c8a3SEzequiel Garcia param = &ubiblock_param[ubiblock_devs]; 1349d54c8a3SEzequiel Garcia if (tokens[1]) { 1359d54c8a3SEzequiel Garcia /* Two parameters: can be 'ubi, vol_id' or 'ubi, vol_name' */ 1369d54c8a3SEzequiel Garcia ret = kstrtoint(tokens[0], 10, ¶m->ubi_num); 1379d54c8a3SEzequiel Garcia if (ret < 0) 1389d54c8a3SEzequiel Garcia return -EINVAL; 1399d54c8a3SEzequiel Garcia 1409d54c8a3SEzequiel Garcia /* Second param can be a number or a name */ 1419d54c8a3SEzequiel Garcia ret = kstrtoint(tokens[1], 10, ¶m->vol_id); 1429d54c8a3SEzequiel Garcia if (ret < 0) { 1439d54c8a3SEzequiel Garcia param->vol_id = -1; 1449d54c8a3SEzequiel Garcia strcpy(param->name, tokens[1]); 1459d54c8a3SEzequiel Garcia } 1469d54c8a3SEzequiel Garcia 1479d54c8a3SEzequiel Garcia } else { 1489d54c8a3SEzequiel Garcia /* One parameter: must be device path */ 1499d54c8a3SEzequiel Garcia strcpy(param->name, tokens[0]); 1509d54c8a3SEzequiel Garcia param->ubi_num = -1; 1519d54c8a3SEzequiel Garcia param->vol_id = -1; 1529d54c8a3SEzequiel Garcia } 1539d54c8a3SEzequiel Garcia 1549d54c8a3SEzequiel Garcia ubiblock_devs++; 1559d54c8a3SEzequiel Garcia 1569d54c8a3SEzequiel Garcia return 0; 1579d54c8a3SEzequiel Garcia } 1589d54c8a3SEzequiel Garcia 1599c27847dSLuis R. Rodriguez static const struct kernel_param_ops ubiblock_param_ops = { 1609d54c8a3SEzequiel Garcia .set = ubiblock_set_param, 1619d54c8a3SEzequiel Garcia }; 1629d54c8a3SEzequiel Garcia module_param_cb(block, &ubiblock_param_ops, NULL, 0); 1639d54c8a3SEzequiel Garcia MODULE_PARM_DESC(block, "Attach block devices to UBI volumes. Parameter format: block=<path|dev,num|dev,name>.\n" 1649d54c8a3SEzequiel Garcia "Multiple \"block\" parameters may be specified.\n" 1659d54c8a3SEzequiel Garcia "UBI volumes may be specified by their number, name, or path to the device node.\n" 1669d54c8a3SEzequiel Garcia "Examples\n" 1679d54c8a3SEzequiel Garcia "Using the UBI volume path:\n" 1689d54c8a3SEzequiel Garcia "ubi.block=/dev/ubi0_0\n" 1699d54c8a3SEzequiel Garcia "Using the UBI device, and the volume name:\n" 1709d54c8a3SEzequiel Garcia "ubi.block=0,rootfs\n" 1719d54c8a3SEzequiel Garcia "Using both UBI device number and UBI volume number:\n" 1729d54c8a3SEzequiel Garcia "ubi.block=0,0\n"); 1739d54c8a3SEzequiel Garcia 1749d54c8a3SEzequiel Garcia static struct ubiblock *find_dev_nolock(int ubi_num, int vol_id) 1759d54c8a3SEzequiel Garcia { 1769d54c8a3SEzequiel Garcia struct ubiblock *dev; 1779d54c8a3SEzequiel Garcia 1789d54c8a3SEzequiel Garcia list_for_each_entry(dev, &ubiblock_devices, list) 1799d54c8a3SEzequiel Garcia if (dev->ubi_num == ubi_num && dev->vol_id == vol_id) 1809d54c8a3SEzequiel Garcia return dev; 1819d54c8a3SEzequiel Garcia return NULL; 1829d54c8a3SEzequiel Garcia } 1839d54c8a3SEzequiel Garcia 184ff1f48eeSRichard Weinberger static int ubiblock_read(struct ubiblock_pdu *pdu) 1859d54c8a3SEzequiel Garcia { 186ff1f48eeSRichard Weinberger int ret, leb, offset, bytes_left, to_read; 187ff1f48eeSRichard Weinberger u64 pos; 188ff1f48eeSRichard Weinberger struct request *req = blk_mq_rq_from_pdu(pdu); 189ff1f48eeSRichard Weinberger struct ubiblock *dev = req->q->queuedata; 1909d54c8a3SEzequiel Garcia 191ff1f48eeSRichard Weinberger to_read = blk_rq_bytes(req); 192ff1f48eeSRichard Weinberger pos = blk_rq_pos(req) << 9; 1939d54c8a3SEzequiel Garcia 1949d54c8a3SEzequiel Garcia /* Get LEB:offset address to read from */ 1959d54c8a3SEzequiel Garcia offset = do_div(pos, dev->leb_size); 1969d54c8a3SEzequiel Garcia leb = pos; 197ff1f48eeSRichard Weinberger bytes_left = to_read; 1989d54c8a3SEzequiel Garcia 1999d54c8a3SEzequiel Garcia while (bytes_left) { 2009d54c8a3SEzequiel Garcia /* 2019d54c8a3SEzequiel Garcia * We can only read one LEB at a time. Therefore if the read 2029d54c8a3SEzequiel Garcia * length is larger than one LEB size, we split the operation. 2039d54c8a3SEzequiel Garcia */ 2049d54c8a3SEzequiel Garcia if (offset + to_read > dev->leb_size) 2059d54c8a3SEzequiel Garcia to_read = dev->leb_size - offset; 2069d54c8a3SEzequiel Garcia 207ff1f48eeSRichard Weinberger ret = ubi_read_sg(dev->desc, leb, &pdu->usgl, offset, to_read); 208ff1f48eeSRichard Weinberger if (ret < 0) 2099d54c8a3SEzequiel Garcia return ret; 2109d54c8a3SEzequiel Garcia 2119d54c8a3SEzequiel Garcia bytes_left -= to_read; 2129d54c8a3SEzequiel Garcia to_read = bytes_left; 2139d54c8a3SEzequiel Garcia leb += 1; 2149d54c8a3SEzequiel Garcia offset = 0; 2159d54c8a3SEzequiel Garcia } 2169d54c8a3SEzequiel Garcia return 0; 2179d54c8a3SEzequiel Garcia } 2189d54c8a3SEzequiel Garcia 2199d54c8a3SEzequiel Garcia static int ubiblock_open(struct block_device *bdev, fmode_t mode) 2209d54c8a3SEzequiel Garcia { 2219d54c8a3SEzequiel Garcia struct ubiblock *dev = bdev->bd_disk->private_data; 2229d54c8a3SEzequiel Garcia int ret; 2239d54c8a3SEzequiel Garcia 2249d54c8a3SEzequiel Garcia mutex_lock(&dev->dev_mutex); 2259d54c8a3SEzequiel Garcia if (dev->refcnt > 0) { 2269d54c8a3SEzequiel Garcia /* 2279d54c8a3SEzequiel Garcia * The volume is already open, just increase the reference 2289d54c8a3SEzequiel Garcia * counter. 2299d54c8a3SEzequiel Garcia */ 2309d54c8a3SEzequiel Garcia goto out_done; 2319d54c8a3SEzequiel Garcia } 2329d54c8a3SEzequiel Garcia 2339d54c8a3SEzequiel Garcia /* 2349d54c8a3SEzequiel Garcia * We want users to be aware they should only mount us as read-only. 2359d54c8a3SEzequiel Garcia * It's just a paranoid check, as write requests will get rejected 2369d54c8a3SEzequiel Garcia * in any case. 2379d54c8a3SEzequiel Garcia */ 2389d54c8a3SEzequiel Garcia if (mode & FMODE_WRITE) { 23978a8dfbaSRomain Izard ret = -EROFS; 2409d54c8a3SEzequiel Garcia goto out_unlock; 2419d54c8a3SEzequiel Garcia } 2429d54c8a3SEzequiel Garcia 2439d54c8a3SEzequiel Garcia dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY); 2449d54c8a3SEzequiel Garcia if (IS_ERR(dev->desc)) { 24532608703STanya Brokhman dev_err(disk_to_dev(dev->gd), "failed to open ubi volume %d_%d", 24632608703STanya Brokhman dev->ubi_num, dev->vol_id); 2479d54c8a3SEzequiel Garcia ret = PTR_ERR(dev->desc); 2489d54c8a3SEzequiel Garcia dev->desc = NULL; 2499d54c8a3SEzequiel Garcia goto out_unlock; 2509d54c8a3SEzequiel Garcia } 2519d54c8a3SEzequiel Garcia 2529d54c8a3SEzequiel Garcia out_done: 2539d54c8a3SEzequiel Garcia dev->refcnt++; 2549d54c8a3SEzequiel Garcia mutex_unlock(&dev->dev_mutex); 2559d54c8a3SEzequiel Garcia return 0; 2569d54c8a3SEzequiel Garcia 2579d54c8a3SEzequiel Garcia out_unlock: 2589d54c8a3SEzequiel Garcia mutex_unlock(&dev->dev_mutex); 2599d54c8a3SEzequiel Garcia return ret; 2609d54c8a3SEzequiel Garcia } 2619d54c8a3SEzequiel Garcia 2629d54c8a3SEzequiel Garcia static void ubiblock_release(struct gendisk *gd, fmode_t mode) 2639d54c8a3SEzequiel Garcia { 2649d54c8a3SEzequiel Garcia struct ubiblock *dev = gd->private_data; 2659d54c8a3SEzequiel Garcia 2669d54c8a3SEzequiel Garcia mutex_lock(&dev->dev_mutex); 2679d54c8a3SEzequiel Garcia dev->refcnt--; 2689d54c8a3SEzequiel Garcia if (dev->refcnt == 0) { 2699d54c8a3SEzequiel Garcia ubi_close_volume(dev->desc); 2709d54c8a3SEzequiel Garcia dev->desc = NULL; 2719d54c8a3SEzequiel Garcia } 2729d54c8a3SEzequiel Garcia mutex_unlock(&dev->dev_mutex); 2739d54c8a3SEzequiel Garcia } 2749d54c8a3SEzequiel Garcia 2759d54c8a3SEzequiel Garcia static int ubiblock_getgeo(struct block_device *bdev, struct hd_geometry *geo) 2769d54c8a3SEzequiel Garcia { 2779d54c8a3SEzequiel Garcia /* Some tools might require this information */ 2789d54c8a3SEzequiel Garcia geo->heads = 1; 2799d54c8a3SEzequiel Garcia geo->cylinders = 1; 2809d54c8a3SEzequiel Garcia geo->sectors = get_capacity(bdev->bd_disk); 2819d54c8a3SEzequiel Garcia geo->start = 0; 2829d54c8a3SEzequiel Garcia return 0; 2839d54c8a3SEzequiel Garcia } 2849d54c8a3SEzequiel Garcia 2859d54c8a3SEzequiel Garcia static const struct block_device_operations ubiblock_ops = { 2869d54c8a3SEzequiel Garcia .owner = THIS_MODULE, 2879d54c8a3SEzequiel Garcia .open = ubiblock_open, 2889d54c8a3SEzequiel Garcia .release = ubiblock_release, 2899d54c8a3SEzequiel Garcia .getgeo = ubiblock_getgeo, 2909d54c8a3SEzequiel Garcia }; 2919d54c8a3SEzequiel Garcia 292ff1f48eeSRichard Weinberger static void ubiblock_do_work(struct work_struct *work) 293ff1f48eeSRichard Weinberger { 294ff1f48eeSRichard Weinberger int ret; 295ff1f48eeSRichard Weinberger struct ubiblock_pdu *pdu = container_of(work, struct ubiblock_pdu, work); 296ff1f48eeSRichard Weinberger struct request *req = blk_mq_rq_from_pdu(pdu); 297ff1f48eeSRichard Weinberger 298ff1f48eeSRichard Weinberger blk_mq_start_request(req); 299832b52a1SRichard Weinberger 300832b52a1SRichard Weinberger /* 301832b52a1SRichard Weinberger * It is safe to ignore the return value of blk_rq_map_sg() because 302832b52a1SRichard Weinberger * the number of sg entries is limited to UBI_MAX_SG_COUNT 303832b52a1SRichard Weinberger * and ubi_read_sg() will check that limit. 304832b52a1SRichard Weinberger */ 305ff1f48eeSRichard Weinberger blk_rq_map_sg(req->q, req, pdu->usgl.sg); 306ff1f48eeSRichard Weinberger 307ff1f48eeSRichard Weinberger ret = ubiblock_read(pdu); 30898fb1ffdSKevin Cernekee rq_flush_dcache_pages(req); 30998fb1ffdSKevin Cernekee 3102a842acaSChristoph Hellwig blk_mq_end_request(req, errno_to_blk_status(ret)); 311ff1f48eeSRichard Weinberger } 312ff1f48eeSRichard Weinberger 313fc17b653SChristoph Hellwig static blk_status_t ubiblock_queue_rq(struct blk_mq_hw_ctx *hctx, 314ff1f48eeSRichard Weinberger const struct blk_mq_queue_data *bd) 315ff1f48eeSRichard Weinberger { 316ff1f48eeSRichard Weinberger struct request *req = bd->rq; 317ff1f48eeSRichard Weinberger struct ubiblock *dev = hctx->queue->queuedata; 318ff1f48eeSRichard Weinberger struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 319ff1f48eeSRichard Weinberger 320aebf526bSChristoph Hellwig switch (req_op(req)) { 321aebf526bSChristoph Hellwig case REQ_OP_READ: 322ff1f48eeSRichard Weinberger ubi_sgl_init(&pdu->usgl); 323ff1f48eeSRichard Weinberger queue_work(dev->wq, &pdu->work); 324fc17b653SChristoph Hellwig return BLK_STS_OK; 325aebf526bSChristoph Hellwig default: 326fc17b653SChristoph Hellwig return BLK_STS_IOERR; 327aebf526bSChristoph Hellwig } 328aebf526bSChristoph Hellwig 329ff1f48eeSRichard Weinberger } 330ff1f48eeSRichard Weinberger 331d6296d39SChristoph Hellwig static int ubiblock_init_request(struct blk_mq_tag_set *set, 332d6296d39SChristoph Hellwig struct request *req, unsigned int hctx_idx, 333ff1f48eeSRichard Weinberger unsigned int numa_node) 334ff1f48eeSRichard Weinberger { 335ff1f48eeSRichard Weinberger struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 336ff1f48eeSRichard Weinberger 337ff1f48eeSRichard Weinberger sg_init_table(pdu->usgl.sg, UBI_MAX_SG_COUNT); 338ff1f48eeSRichard Weinberger INIT_WORK(&pdu->work, ubiblock_do_work); 339ff1f48eeSRichard Weinberger 340ff1f48eeSRichard Weinberger return 0; 341ff1f48eeSRichard Weinberger } 342ff1f48eeSRichard Weinberger 343f363b089SEric Biggers static const struct blk_mq_ops ubiblock_mq_ops = { 344ff1f48eeSRichard Weinberger .queue_rq = ubiblock_queue_rq, 345ff1f48eeSRichard Weinberger .init_request = ubiblock_init_request, 346ff1f48eeSRichard Weinberger }; 347ff1f48eeSRichard Weinberger 348*e46131b9SRichard Weinberger static int calc_disk_capacity(struct ubi_volume_info *vi, u64 *disk_capacity) 349*e46131b9SRichard Weinberger { 350*e46131b9SRichard Weinberger u64 size = vi->used_bytes >> 9; 351*e46131b9SRichard Weinberger 352*e46131b9SRichard Weinberger if (vi->used_bytes % 512) { 353*e46131b9SRichard Weinberger pr_warn("UBI: block: volume size is not a multiple of 512, " 354*e46131b9SRichard Weinberger "last %llu bytes are ignored!\n", 355*e46131b9SRichard Weinberger vi->used_bytes - (size << 9)); 356*e46131b9SRichard Weinberger } 357*e46131b9SRichard Weinberger 358*e46131b9SRichard Weinberger if ((sector_t)size != size) 359*e46131b9SRichard Weinberger return -EFBIG; 360*e46131b9SRichard Weinberger 361*e46131b9SRichard Weinberger *disk_capacity = size; 362*e46131b9SRichard Weinberger 363*e46131b9SRichard Weinberger return 0; 364*e46131b9SRichard Weinberger } 365*e46131b9SRichard Weinberger 3664d283ee2SArtem Bityutskiy int ubiblock_create(struct ubi_volume_info *vi) 3679d54c8a3SEzequiel Garcia { 3689d54c8a3SEzequiel Garcia struct ubiblock *dev; 3699d54c8a3SEzequiel Garcia struct gendisk *gd; 370*e46131b9SRichard Weinberger u64 disk_capacity; 3719d54c8a3SEzequiel Garcia int ret; 3729d54c8a3SEzequiel Garcia 373*e46131b9SRichard Weinberger ret = calc_disk_capacity(vi, &disk_capacity); 374*e46131b9SRichard Weinberger if (ret) { 375*e46131b9SRichard Weinberger return ret; 376*e46131b9SRichard Weinberger } 377*e46131b9SRichard Weinberger 3789d54c8a3SEzequiel Garcia /* Check that the volume isn't already handled */ 3799d54c8a3SEzequiel Garcia mutex_lock(&devices_mutex); 3809d54c8a3SEzequiel Garcia if (find_dev_nolock(vi->ubi_num, vi->vol_id)) { 3817f29ae9fSBradley Bolen ret = -EEXIST; 3827f29ae9fSBradley Bolen goto out_unlock; 3839d54c8a3SEzequiel Garcia } 3849d54c8a3SEzequiel Garcia 3859d54c8a3SEzequiel Garcia dev = kzalloc(sizeof(struct ubiblock), GFP_KERNEL); 3867f29ae9fSBradley Bolen if (!dev) { 3877f29ae9fSBradley Bolen ret = -ENOMEM; 3887f29ae9fSBradley Bolen goto out_unlock; 3897f29ae9fSBradley Bolen } 3909d54c8a3SEzequiel Garcia 3919d54c8a3SEzequiel Garcia mutex_init(&dev->dev_mutex); 3929d54c8a3SEzequiel Garcia 3939d54c8a3SEzequiel Garcia dev->ubi_num = vi->ubi_num; 3949d54c8a3SEzequiel Garcia dev->vol_id = vi->vol_id; 3959d54c8a3SEzequiel Garcia dev->leb_size = vi->usable_leb_size; 3969d54c8a3SEzequiel Garcia 3979d54c8a3SEzequiel Garcia /* Initialize the gendisk of this ubiblock device */ 3989d54c8a3SEzequiel Garcia gd = alloc_disk(1); 3999d54c8a3SEzequiel Garcia if (!gd) { 400b62fc462SBen Dooks pr_err("UBI: block: alloc_disk failed\n"); 4019d54c8a3SEzequiel Garcia ret = -ENODEV; 4029d54c8a3SEzequiel Garcia goto out_free_dev; 4039d54c8a3SEzequiel Garcia } 4049d54c8a3SEzequiel Garcia 4059d54c8a3SEzequiel Garcia gd->fops = &ubiblock_ops; 4069d54c8a3SEzequiel Garcia gd->major = ubiblock_major; 4072bf50d42SDan Ehrenberg gd->first_minor = idr_alloc(&ubiblock_minor_idr, dev, 0, 0, GFP_KERNEL); 4082bf50d42SDan Ehrenberg if (gd->first_minor < 0) { 4092bf50d42SDan Ehrenberg dev_err(disk_to_dev(gd), 4102bf50d42SDan Ehrenberg "block: dynamic minor allocation failed"); 4112bf50d42SDan Ehrenberg ret = -ENODEV; 4122bf50d42SDan Ehrenberg goto out_put_disk; 4132bf50d42SDan Ehrenberg } 4149d54c8a3SEzequiel Garcia gd->private_data = dev; 4159d54c8a3SEzequiel Garcia sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); 4169d54c8a3SEzequiel Garcia set_capacity(gd, disk_capacity); 4179d54c8a3SEzequiel Garcia dev->gd = gd; 4189d54c8a3SEzequiel Garcia 419ff1f48eeSRichard Weinberger dev->tag_set.ops = &ubiblock_mq_ops; 420ff1f48eeSRichard Weinberger dev->tag_set.queue_depth = 64; 421ff1f48eeSRichard Weinberger dev->tag_set.numa_node = NUMA_NO_NODE; 422ff1f48eeSRichard Weinberger dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; 423ff1f48eeSRichard Weinberger dev->tag_set.cmd_size = sizeof(struct ubiblock_pdu); 424ff1f48eeSRichard Weinberger dev->tag_set.driver_data = dev; 425ff1f48eeSRichard Weinberger dev->tag_set.nr_hw_queues = 1; 426ff1f48eeSRichard Weinberger 427ff1f48eeSRichard Weinberger ret = blk_mq_alloc_tag_set(&dev->tag_set); 428ff1f48eeSRichard Weinberger if (ret) { 429ff1f48eeSRichard Weinberger dev_err(disk_to_dev(dev->gd), "blk_mq_alloc_tag_set failed"); 4302bf50d42SDan Ehrenberg goto out_remove_minor; 4319d54c8a3SEzequiel Garcia } 4329d54c8a3SEzequiel Garcia 433ff1f48eeSRichard Weinberger dev->rq = blk_mq_init_queue(&dev->tag_set); 4348168b9bbSDan Carpenter if (IS_ERR(dev->rq)) { 435ff1f48eeSRichard Weinberger dev_err(disk_to_dev(gd), "blk_mq_init_queue failed"); 4368168b9bbSDan Carpenter ret = PTR_ERR(dev->rq); 437ff1f48eeSRichard Weinberger goto out_free_tags; 438ff1f48eeSRichard Weinberger } 439ff1f48eeSRichard Weinberger blk_queue_max_segments(dev->rq, UBI_MAX_SG_COUNT); 440ff1f48eeSRichard Weinberger 4419d54c8a3SEzequiel Garcia dev->rq->queuedata = dev; 4429d54c8a3SEzequiel Garcia dev->gd->queue = dev->rq; 4439d54c8a3SEzequiel Garcia 4449d54c8a3SEzequiel Garcia /* 4459d54c8a3SEzequiel Garcia * Create one workqueue per volume (per registered block device). 4469d54c8a3SEzequiel Garcia * Rembember workqueues are cheap, they're not threads. 4479d54c8a3SEzequiel Garcia */ 448bebfef15SKees Cook dev->wq = alloc_workqueue("%s", 0, 0, gd->disk_name); 449151d6b21SHelmut Schaa if (!dev->wq) { 450151d6b21SHelmut Schaa ret = -ENOMEM; 4519d54c8a3SEzequiel Garcia goto out_free_queue; 452151d6b21SHelmut Schaa } 4539d54c8a3SEzequiel Garcia 4549d54c8a3SEzequiel Garcia list_add_tail(&dev->list, &ubiblock_devices); 4559d54c8a3SEzequiel Garcia 4569d54c8a3SEzequiel Garcia /* Must be the last step: anyone can call file ops from now on */ 4579d54c8a3SEzequiel Garcia add_disk(dev->gd); 45832608703STanya Brokhman dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", 45932608703STanya Brokhman dev->ubi_num, dev->vol_id, vi->name); 4607f29ae9fSBradley Bolen mutex_unlock(&devices_mutex); 4619d54c8a3SEzequiel Garcia return 0; 4629d54c8a3SEzequiel Garcia 4639d54c8a3SEzequiel Garcia out_free_queue: 4649d54c8a3SEzequiel Garcia blk_cleanup_queue(dev->rq); 465ff1f48eeSRichard Weinberger out_free_tags: 466ff1f48eeSRichard Weinberger blk_mq_free_tag_set(&dev->tag_set); 4672bf50d42SDan Ehrenberg out_remove_minor: 4682bf50d42SDan Ehrenberg idr_remove(&ubiblock_minor_idr, gd->first_minor); 4699d54c8a3SEzequiel Garcia out_put_disk: 4709d54c8a3SEzequiel Garcia put_disk(dev->gd); 4719d54c8a3SEzequiel Garcia out_free_dev: 4729d54c8a3SEzequiel Garcia kfree(dev); 4737f29ae9fSBradley Bolen out_unlock: 4747f29ae9fSBradley Bolen mutex_unlock(&devices_mutex); 4759d54c8a3SEzequiel Garcia 4769d54c8a3SEzequiel Garcia return ret; 4779d54c8a3SEzequiel Garcia } 4789d54c8a3SEzequiel Garcia 4799d54c8a3SEzequiel Garcia static void ubiblock_cleanup(struct ubiblock *dev) 4809d54c8a3SEzequiel Garcia { 481ff1f48eeSRichard Weinberger /* Stop new requests to arrive */ 4829d54c8a3SEzequiel Garcia del_gendisk(dev->gd); 483ff1f48eeSRichard Weinberger /* Flush pending work */ 484ff1f48eeSRichard Weinberger destroy_workqueue(dev->wq); 485ff1f48eeSRichard Weinberger /* Finally destroy the blk queue */ 4869d54c8a3SEzequiel Garcia blk_cleanup_queue(dev->rq); 487ff1f48eeSRichard Weinberger blk_mq_free_tag_set(&dev->tag_set); 48832608703STanya Brokhman dev_info(disk_to_dev(dev->gd), "released"); 4892bf50d42SDan Ehrenberg idr_remove(&ubiblock_minor_idr, dev->gd->first_minor); 4909d54c8a3SEzequiel Garcia put_disk(dev->gd); 4919d54c8a3SEzequiel Garcia } 4929d54c8a3SEzequiel Garcia 4934d283ee2SArtem Bityutskiy int ubiblock_remove(struct ubi_volume_info *vi) 4949d54c8a3SEzequiel Garcia { 4959d54c8a3SEzequiel Garcia struct ubiblock *dev; 4967f29ae9fSBradley Bolen int ret; 4979d54c8a3SEzequiel Garcia 4989d54c8a3SEzequiel Garcia mutex_lock(&devices_mutex); 4999d54c8a3SEzequiel Garcia dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 5009d54c8a3SEzequiel Garcia if (!dev) { 5017f29ae9fSBradley Bolen ret = -ENODEV; 5027f29ae9fSBradley Bolen goto out_unlock; 5039d54c8a3SEzequiel Garcia } 5049d54c8a3SEzequiel Garcia 5059d54c8a3SEzequiel Garcia /* Found a device, let's lock it so we can check if it's busy */ 5069d54c8a3SEzequiel Garcia mutex_lock(&dev->dev_mutex); 5079d54c8a3SEzequiel Garcia if (dev->refcnt > 0) { 5087f29ae9fSBradley Bolen ret = -EBUSY; 5097f29ae9fSBradley Bolen goto out_unlock_dev; 5109d54c8a3SEzequiel Garcia } 5119d54c8a3SEzequiel Garcia 5129d54c8a3SEzequiel Garcia /* Remove from device list */ 5139d54c8a3SEzequiel Garcia list_del(&dev->list); 5149d54c8a3SEzequiel Garcia ubiblock_cleanup(dev); 5159d54c8a3SEzequiel Garcia mutex_unlock(&dev->dev_mutex); 5167f29ae9fSBradley Bolen mutex_unlock(&devices_mutex); 5177f29ae9fSBradley Bolen 5189d54c8a3SEzequiel Garcia kfree(dev); 5199d54c8a3SEzequiel Garcia return 0; 5207f29ae9fSBradley Bolen 5217f29ae9fSBradley Bolen out_unlock_dev: 5227f29ae9fSBradley Bolen mutex_unlock(&dev->dev_mutex); 5237f29ae9fSBradley Bolen out_unlock: 5247f29ae9fSBradley Bolen mutex_unlock(&devices_mutex); 5257f29ae9fSBradley Bolen return ret; 5269d54c8a3SEzequiel Garcia } 5279d54c8a3SEzequiel Garcia 528495f2bf6SEzequiel Garcia static int ubiblock_resize(struct ubi_volume_info *vi) 5299d54c8a3SEzequiel Garcia { 5309d54c8a3SEzequiel Garcia struct ubiblock *dev; 531*e46131b9SRichard Weinberger u64 disk_capacity; 532*e46131b9SRichard Weinberger int ret; 5339d54c8a3SEzequiel Garcia 5349d54c8a3SEzequiel Garcia /* 5359d54c8a3SEzequiel Garcia * Need to lock the device list until we stop using the device, 5364d283ee2SArtem Bityutskiy * otherwise the device struct might get released in 5374d283ee2SArtem Bityutskiy * 'ubiblock_remove()'. 5389d54c8a3SEzequiel Garcia */ 5399d54c8a3SEzequiel Garcia mutex_lock(&devices_mutex); 5409d54c8a3SEzequiel Garcia dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 5419d54c8a3SEzequiel Garcia if (!dev) { 5429d54c8a3SEzequiel Garcia mutex_unlock(&devices_mutex); 543495f2bf6SEzequiel Garcia return -ENODEV; 5449d54c8a3SEzequiel Garcia } 545*e46131b9SRichard Weinberger 546*e46131b9SRichard Weinberger ret = calc_disk_capacity(vi, &disk_capacity); 547*e46131b9SRichard Weinberger if (ret) { 5483df77072SColin Ian King mutex_unlock(&devices_mutex); 549*e46131b9SRichard Weinberger if (ret == -EFBIG) { 550*e46131b9SRichard Weinberger dev_warn(disk_to_dev(dev->gd), 551*e46131b9SRichard Weinberger "the volume is too big (%d LEBs), cannot resize", 55232608703STanya Brokhman vi->size); 553*e46131b9SRichard Weinberger } 554*e46131b9SRichard Weinberger return ret; 5553df77072SColin Ian King } 5569d54c8a3SEzequiel Garcia 5579d54c8a3SEzequiel Garcia mutex_lock(&dev->dev_mutex); 55806d9c290SEzequiel Garcia 55906d9c290SEzequiel Garcia if (get_capacity(dev->gd) != disk_capacity) { 5609d54c8a3SEzequiel Garcia set_capacity(dev->gd, disk_capacity); 56132608703STanya Brokhman dev_info(disk_to_dev(dev->gd), "resized to %lld bytes", 56206d9c290SEzequiel Garcia vi->used_bytes); 56306d9c290SEzequiel Garcia } 5649d54c8a3SEzequiel Garcia mutex_unlock(&dev->dev_mutex); 5659d54c8a3SEzequiel Garcia mutex_unlock(&devices_mutex); 566495f2bf6SEzequiel Garcia return 0; 5679d54c8a3SEzequiel Garcia } 5689d54c8a3SEzequiel Garcia 5699d54c8a3SEzequiel Garcia static int ubiblock_notify(struct notifier_block *nb, 5709d54c8a3SEzequiel Garcia unsigned long notification_type, void *ns_ptr) 5719d54c8a3SEzequiel Garcia { 5729d54c8a3SEzequiel Garcia struct ubi_notification *nt = ns_ptr; 5739d54c8a3SEzequiel Garcia 5749d54c8a3SEzequiel Garcia switch (notification_type) { 5759d54c8a3SEzequiel Garcia case UBI_VOLUME_ADDED: 5769d54c8a3SEzequiel Garcia /* 5774d283ee2SArtem Bityutskiy * We want to enforce explicit block device creation for 5789d54c8a3SEzequiel Garcia * volumes, so when a volume is added we do nothing. 5799d54c8a3SEzequiel Garcia */ 5809d54c8a3SEzequiel Garcia break; 5819d54c8a3SEzequiel Garcia case UBI_VOLUME_REMOVED: 5824d283ee2SArtem Bityutskiy ubiblock_remove(&nt->vi); 5839d54c8a3SEzequiel Garcia break; 5849d54c8a3SEzequiel Garcia case UBI_VOLUME_RESIZED: 5859d54c8a3SEzequiel Garcia ubiblock_resize(&nt->vi); 5869d54c8a3SEzequiel Garcia break; 58706d9c290SEzequiel Garcia case UBI_VOLUME_UPDATED: 58806d9c290SEzequiel Garcia /* 58906d9c290SEzequiel Garcia * If the volume is static, a content update might mean the 59006d9c290SEzequiel Garcia * size (i.e. used_bytes) was also changed. 59106d9c290SEzequiel Garcia */ 59206d9c290SEzequiel Garcia if (nt->vi.vol_type == UBI_STATIC_VOLUME) 59306d9c290SEzequiel Garcia ubiblock_resize(&nt->vi); 59406d9c290SEzequiel Garcia break; 5959d54c8a3SEzequiel Garcia default: 5969d54c8a3SEzequiel Garcia break; 5979d54c8a3SEzequiel Garcia } 5989d54c8a3SEzequiel Garcia return NOTIFY_OK; 5999d54c8a3SEzequiel Garcia } 6009d54c8a3SEzequiel Garcia 6019d54c8a3SEzequiel Garcia static struct notifier_block ubiblock_notifier = { 6029d54c8a3SEzequiel Garcia .notifier_call = ubiblock_notify, 6039d54c8a3SEzequiel Garcia }; 6049d54c8a3SEzequiel Garcia 6059d54c8a3SEzequiel Garcia static struct ubi_volume_desc * __init 6069d54c8a3SEzequiel Garcia open_volume_desc(const char *name, int ubi_num, int vol_id) 6079d54c8a3SEzequiel Garcia { 6089d54c8a3SEzequiel Garcia if (ubi_num == -1) 6099d54c8a3SEzequiel Garcia /* No ubi num, name must be a vol device path */ 6109d54c8a3SEzequiel Garcia return ubi_open_volume_path(name, UBI_READONLY); 6119d54c8a3SEzequiel Garcia else if (vol_id == -1) 6129d54c8a3SEzequiel Garcia /* No vol_id, must be vol_name */ 6139d54c8a3SEzequiel Garcia return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); 6149d54c8a3SEzequiel Garcia else 6159d54c8a3SEzequiel Garcia return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); 6169d54c8a3SEzequiel Garcia } 6179d54c8a3SEzequiel Garcia 6181440061bSDan Ehrenberg static void __init ubiblock_create_from_param(void) 6199d54c8a3SEzequiel Garcia { 6201440061bSDan Ehrenberg int i, ret = 0; 6219d54c8a3SEzequiel Garcia struct ubiblock_param *p; 6229d54c8a3SEzequiel Garcia struct ubi_volume_desc *desc; 6239d54c8a3SEzequiel Garcia struct ubi_volume_info vi; 6249d54c8a3SEzequiel Garcia 6251440061bSDan Ehrenberg /* 6261440061bSDan Ehrenberg * If there is an error creating one of the ubiblocks, continue on to 6271440061bSDan Ehrenberg * create the following ubiblocks. This helps in a circumstance where 6281440061bSDan Ehrenberg * the kernel command-line specifies multiple block devices and some 6291440061bSDan Ehrenberg * may be broken, but we still want the working ones to come up. 6301440061bSDan Ehrenberg */ 6319d54c8a3SEzequiel Garcia for (i = 0; i < ubiblock_devs; i++) { 6329d54c8a3SEzequiel Garcia p = &ubiblock_param[i]; 6339d54c8a3SEzequiel Garcia 6349d54c8a3SEzequiel Garcia desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); 6359d54c8a3SEzequiel Garcia if (IS_ERR(desc)) { 6361440061bSDan Ehrenberg pr_err( 637b62fc462SBen Dooks "UBI: block: can't open volume on ubi%d_%d, err=%ld\n", 6381440061bSDan Ehrenberg p->ubi_num, p->vol_id, PTR_ERR(desc)); 6391440061bSDan Ehrenberg continue; 6409d54c8a3SEzequiel Garcia } 6419d54c8a3SEzequiel Garcia 6429d54c8a3SEzequiel Garcia ubi_get_volume_info(desc, &vi); 6439d54c8a3SEzequiel Garcia ubi_close_volume(desc); 6449d54c8a3SEzequiel Garcia 6454d283ee2SArtem Bityutskiy ret = ubiblock_create(&vi); 6469d54c8a3SEzequiel Garcia if (ret) { 6471440061bSDan Ehrenberg pr_err( 648b62fc462SBen Dooks "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", 6491440061bSDan Ehrenberg vi.name, p->ubi_num, p->vol_id, ret); 6501440061bSDan Ehrenberg continue; 6519d54c8a3SEzequiel Garcia } 6529d54c8a3SEzequiel Garcia } 6539d54c8a3SEzequiel Garcia } 6549d54c8a3SEzequiel Garcia 6554d283ee2SArtem Bityutskiy static void ubiblock_remove_all(void) 6569d54c8a3SEzequiel Garcia { 6579d54c8a3SEzequiel Garcia struct ubiblock *next; 6589d54c8a3SEzequiel Garcia struct ubiblock *dev; 6599d54c8a3SEzequiel Garcia 6607f29ae9fSBradley Bolen mutex_lock(&devices_mutex); 6619d54c8a3SEzequiel Garcia list_for_each_entry_safe(dev, next, &ubiblock_devices, list) { 6629d54c8a3SEzequiel Garcia /* The module is being forcefully removed */ 6639d54c8a3SEzequiel Garcia WARN_ON(dev->desc); 6649d54c8a3SEzequiel Garcia /* Remove from device list */ 6659d54c8a3SEzequiel Garcia list_del(&dev->list); 6669d54c8a3SEzequiel Garcia ubiblock_cleanup(dev); 6679d54c8a3SEzequiel Garcia kfree(dev); 6689d54c8a3SEzequiel Garcia } 6697f29ae9fSBradley Bolen mutex_unlock(&devices_mutex); 6709d54c8a3SEzequiel Garcia } 6719d54c8a3SEzequiel Garcia 6729d54c8a3SEzequiel Garcia int __init ubiblock_init(void) 6739d54c8a3SEzequiel Garcia { 6749d54c8a3SEzequiel Garcia int ret; 6759d54c8a3SEzequiel Garcia 6769d54c8a3SEzequiel Garcia ubiblock_major = register_blkdev(0, "ubiblock"); 6779d54c8a3SEzequiel Garcia if (ubiblock_major < 0) 6789d54c8a3SEzequiel Garcia return ubiblock_major; 6799d54c8a3SEzequiel Garcia 6801440061bSDan Ehrenberg /* 6811440061bSDan Ehrenberg * Attach block devices from 'block=' module param. 6821440061bSDan Ehrenberg * Even if one block device in the param list fails to come up, 6831440061bSDan Ehrenberg * still allow the module to load and leave any others up. 6841440061bSDan Ehrenberg */ 6851440061bSDan Ehrenberg ubiblock_create_from_param(); 6869d54c8a3SEzequiel Garcia 6879d54c8a3SEzequiel Garcia /* 6884d283ee2SArtem Bityutskiy * Block devices are only created upon user requests, so we ignore 6894d283ee2SArtem Bityutskiy * existing volumes. 6909d54c8a3SEzequiel Garcia */ 6919d54c8a3SEzequiel Garcia ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); 6929d54c8a3SEzequiel Garcia if (ret) 6939d54c8a3SEzequiel Garcia goto err_unreg; 6949d54c8a3SEzequiel Garcia return 0; 6959d54c8a3SEzequiel Garcia 6969d54c8a3SEzequiel Garcia err_unreg: 6979d54c8a3SEzequiel Garcia unregister_blkdev(ubiblock_major, "ubiblock"); 6984d283ee2SArtem Bityutskiy ubiblock_remove_all(); 6999d54c8a3SEzequiel Garcia return ret; 7009d54c8a3SEzequiel Garcia } 7019d54c8a3SEzequiel Garcia 7029d54c8a3SEzequiel Garcia void __exit ubiblock_exit(void) 7039d54c8a3SEzequiel Garcia { 7049d54c8a3SEzequiel Garcia ubi_unregister_volume_notifier(&ubiblock_notifier); 7054d283ee2SArtem Bityutskiy ubiblock_remove_all(); 7069d54c8a3SEzequiel Garcia unregister_blkdev(ubiblock_major, "ubiblock"); 7079d54c8a3SEzequiel Garcia } 708