18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Ezequiel Garcia 48c2ecf20Sopenharmony_ci * Copyright (c) 2011 Free Electrons 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Driver parameter handling strongly based on drivers/mtd/ubi/build.c 78c2ecf20Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006 88c2ecf20Sopenharmony_ci * Copyright (c) Nokia Corporation, 2007 98c2ecf20Sopenharmony_ci * Authors: Artem Bityutskiy, Frank Haverkamp 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * Read-only block devices on top of UBI volumes 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * A simple implementation to allow a block device to be layered on top of a 168c2ecf20Sopenharmony_ci * UBI volume. The implementation is provided by creating a static 1-to-1 178c2ecf20Sopenharmony_ci * mapping between the block device and the UBI volume. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * The addressed byte is obtained from the addressed block sector, which is 208c2ecf20Sopenharmony_ci * mapped linearly into the corresponding LEB: 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * LEB number = addressed byte / LEB size 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * This feature is compiled in the UBI core, and adds a 'block' parameter 258c2ecf20Sopenharmony_ci * to allow early creation of block devices on top of UBI volumes. Runtime 268c2ecf20Sopenharmony_ci * block creation/removal for UBI volumes is provided through two UBI ioctls: 278c2ecf20Sopenharmony_ci * UBI_IOCVOLCRBLK and UBI_IOCVOLRMBLK. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/init.h> 328c2ecf20Sopenharmony_ci#include <linux/err.h> 338c2ecf20Sopenharmony_ci#include <linux/kernel.h> 348c2ecf20Sopenharmony_ci#include <linux/list.h> 358c2ecf20Sopenharmony_ci#include <linux/mutex.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci#include <linux/mtd/ubi.h> 388c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 398c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 408c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 418c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 428c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 438c2ecf20Sopenharmony_ci#include <linux/idr.h> 448c2ecf20Sopenharmony_ci#include <asm/div64.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include "ubi-media.h" 478c2ecf20Sopenharmony_ci#include "ubi.h" 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Maximum number of supported devices */ 508c2ecf20Sopenharmony_ci#define UBIBLOCK_MAX_DEVICES 32 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Maximum length of the 'block=' parameter */ 538c2ecf20Sopenharmony_ci#define UBIBLOCK_PARAM_LEN 63 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Maximum number of comma-separated items in the 'block=' parameter */ 568c2ecf20Sopenharmony_ci#define UBIBLOCK_PARAM_COUNT 2 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct ubiblock_param { 598c2ecf20Sopenharmony_ci int ubi_num; 608c2ecf20Sopenharmony_ci int vol_id; 618c2ecf20Sopenharmony_ci char name[UBIBLOCK_PARAM_LEN+1]; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct ubiblock_pdu { 658c2ecf20Sopenharmony_ci struct work_struct work; 668c2ecf20Sopenharmony_ci struct ubi_sgl usgl; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Numbers of elements set in the @ubiblock_param array */ 708c2ecf20Sopenharmony_cistatic int ubiblock_devs __initdata; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* MTD devices specification parameters */ 738c2ecf20Sopenharmony_cistatic struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct ubiblock { 768c2ecf20Sopenharmony_ci struct ubi_volume_desc *desc; 778c2ecf20Sopenharmony_ci int ubi_num; 788c2ecf20Sopenharmony_ci int vol_id; 798c2ecf20Sopenharmony_ci int refcnt; 808c2ecf20Sopenharmony_ci int leb_size; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci struct gendisk *gd; 838c2ecf20Sopenharmony_ci struct request_queue *rq; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci struct workqueue_struct *wq; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci struct mutex dev_mutex; 888c2ecf20Sopenharmony_ci struct list_head list; 898c2ecf20Sopenharmony_ci struct blk_mq_tag_set tag_set; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Linked list of all ubiblock instances */ 938c2ecf20Sopenharmony_cistatic LIST_HEAD(ubiblock_devices); 948c2ecf20Sopenharmony_cistatic DEFINE_IDR(ubiblock_minor_idr); 958c2ecf20Sopenharmony_ci/* Protects ubiblock_devices and ubiblock_minor_idr */ 968c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(devices_mutex); 978c2ecf20Sopenharmony_cistatic int ubiblock_major; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int __init ubiblock_set_param(const char *val, 1008c2ecf20Sopenharmony_ci const struct kernel_param *kp) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci int i, ret; 1038c2ecf20Sopenharmony_ci size_t len; 1048c2ecf20Sopenharmony_ci struct ubiblock_param *param; 1058c2ecf20Sopenharmony_ci char buf[UBIBLOCK_PARAM_LEN]; 1068c2ecf20Sopenharmony_ci char *pbuf = &buf[0]; 1078c2ecf20Sopenharmony_ci char *tokens[UBIBLOCK_PARAM_COUNT]; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!val) 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci len = strnlen(val, UBIBLOCK_PARAM_LEN); 1138c2ecf20Sopenharmony_ci if (len == 0) { 1148c2ecf20Sopenharmony_ci pr_warn("UBI: block: empty 'block=' parameter - ignored\n"); 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (len == UBIBLOCK_PARAM_LEN) { 1198c2ecf20Sopenharmony_ci pr_err("UBI: block: parameter \"%s\" is too long, max. is %d\n", 1208c2ecf20Sopenharmony_ci val, UBIBLOCK_PARAM_LEN); 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci strcpy(buf, val); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Get rid of the final newline */ 1278c2ecf20Sopenharmony_ci if (buf[len - 1] == '\n') 1288c2ecf20Sopenharmony_ci buf[len - 1] = '\0'; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci for (i = 0; i < UBIBLOCK_PARAM_COUNT; i++) 1318c2ecf20Sopenharmony_ci tokens[i] = strsep(&pbuf, ","); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci param = &ubiblock_param[ubiblock_devs]; 1348c2ecf20Sopenharmony_ci if (tokens[1]) { 1358c2ecf20Sopenharmony_ci /* Two parameters: can be 'ubi, vol_id' or 'ubi, vol_name' */ 1368c2ecf20Sopenharmony_ci ret = kstrtoint(tokens[0], 10, ¶m->ubi_num); 1378c2ecf20Sopenharmony_ci if (ret < 0) 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Second param can be a number or a name */ 1418c2ecf20Sopenharmony_ci ret = kstrtoint(tokens[1], 10, ¶m->vol_id); 1428c2ecf20Sopenharmony_ci if (ret < 0) { 1438c2ecf20Sopenharmony_ci param->vol_id = -1; 1448c2ecf20Sopenharmony_ci strcpy(param->name, tokens[1]); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci } else { 1488c2ecf20Sopenharmony_ci /* One parameter: must be device path */ 1498c2ecf20Sopenharmony_ci strcpy(param->name, tokens[0]); 1508c2ecf20Sopenharmony_ci param->ubi_num = -1; 1518c2ecf20Sopenharmony_ci param->vol_id = -1; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ubiblock_devs++; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct kernel_param_ops ubiblock_param_ops = { 1608c2ecf20Sopenharmony_ci .set = ubiblock_set_param, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_cimodule_param_cb(block, &ubiblock_param_ops, NULL, 0); 1638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(block, "Attach block devices to UBI volumes. Parameter format: block=<path|dev,num|dev,name>.\n" 1648c2ecf20Sopenharmony_ci "Multiple \"block\" parameters may be specified.\n" 1658c2ecf20Sopenharmony_ci "UBI volumes may be specified by their number, name, or path to the device node.\n" 1668c2ecf20Sopenharmony_ci "Examples\n" 1678c2ecf20Sopenharmony_ci "Using the UBI volume path:\n" 1688c2ecf20Sopenharmony_ci "ubi.block=/dev/ubi0_0\n" 1698c2ecf20Sopenharmony_ci "Using the UBI device, and the volume name:\n" 1708c2ecf20Sopenharmony_ci "ubi.block=0,rootfs\n" 1718c2ecf20Sopenharmony_ci "Using both UBI device number and UBI volume number:\n" 1728c2ecf20Sopenharmony_ci "ubi.block=0,0\n"); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic struct ubiblock *find_dev_nolock(int ubi_num, int vol_id) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct ubiblock *dev; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci list_for_each_entry(dev, &ubiblock_devices, list) 1798c2ecf20Sopenharmony_ci if (dev->ubi_num == ubi_num && dev->vol_id == vol_id) 1808c2ecf20Sopenharmony_ci return dev; 1818c2ecf20Sopenharmony_ci return NULL; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int ubiblock_read(struct ubiblock_pdu *pdu) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int ret, leb, offset, bytes_left, to_read; 1878c2ecf20Sopenharmony_ci u64 pos; 1888c2ecf20Sopenharmony_ci struct request *req = blk_mq_rq_from_pdu(pdu); 1898c2ecf20Sopenharmony_ci struct ubiblock *dev = req->q->queuedata; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci to_read = blk_rq_bytes(req); 1928c2ecf20Sopenharmony_ci pos = blk_rq_pos(req) << 9; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Get LEB:offset address to read from */ 1958c2ecf20Sopenharmony_ci offset = do_div(pos, dev->leb_size); 1968c2ecf20Sopenharmony_ci leb = pos; 1978c2ecf20Sopenharmony_ci bytes_left = to_read; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci while (bytes_left) { 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * We can only read one LEB at a time. Therefore if the read 2028c2ecf20Sopenharmony_ci * length is larger than one LEB size, we split the operation. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci if (offset + to_read > dev->leb_size) 2058c2ecf20Sopenharmony_ci to_read = dev->leb_size - offset; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = ubi_read_sg(dev->desc, leb, &pdu->usgl, offset, to_read); 2088c2ecf20Sopenharmony_ci if (ret < 0) 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci bytes_left -= to_read; 2128c2ecf20Sopenharmony_ci to_read = bytes_left; 2138c2ecf20Sopenharmony_ci leb += 1; 2148c2ecf20Sopenharmony_ci offset = 0; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int ubiblock_open(struct block_device *bdev, fmode_t mode) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct ubiblock *dev = bdev->bd_disk->private_data; 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci mutex_lock(&dev->dev_mutex); 2258c2ecf20Sopenharmony_ci if (dev->refcnt > 0) { 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * The volume is already open, just increase the reference 2288c2ecf20Sopenharmony_ci * counter. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci goto out_done; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * We want users to be aware they should only mount us as read-only. 2358c2ecf20Sopenharmony_ci * It's just a paranoid check, as write requests will get rejected 2368c2ecf20Sopenharmony_ci * in any case. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (mode & FMODE_WRITE) { 2398c2ecf20Sopenharmony_ci ret = -EROFS; 2408c2ecf20Sopenharmony_ci goto out_unlock; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY); 2448c2ecf20Sopenharmony_ci if (IS_ERR(dev->desc)) { 2458c2ecf20Sopenharmony_ci dev_err(disk_to_dev(dev->gd), "failed to open ubi volume %d_%d", 2468c2ecf20Sopenharmony_ci dev->ubi_num, dev->vol_id); 2478c2ecf20Sopenharmony_ci ret = PTR_ERR(dev->desc); 2488c2ecf20Sopenharmony_ci dev->desc = NULL; 2498c2ecf20Sopenharmony_ci goto out_unlock; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciout_done: 2538c2ecf20Sopenharmony_ci dev->refcnt++; 2548c2ecf20Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciout_unlock: 2588c2ecf20Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void ubiblock_release(struct gendisk *gd, fmode_t mode) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct ubiblock *dev = gd->private_data; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci mutex_lock(&dev->dev_mutex); 2678c2ecf20Sopenharmony_ci dev->refcnt--; 2688c2ecf20Sopenharmony_ci if (dev->refcnt == 0) { 2698c2ecf20Sopenharmony_ci ubi_close_volume(dev->desc); 2708c2ecf20Sopenharmony_ci dev->desc = NULL; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int ubiblock_getgeo(struct block_device *bdev, struct hd_geometry *geo) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci /* Some tools might require this information */ 2788c2ecf20Sopenharmony_ci geo->heads = 1; 2798c2ecf20Sopenharmony_ci geo->cylinders = 1; 2808c2ecf20Sopenharmony_ci geo->sectors = get_capacity(bdev->bd_disk); 2818c2ecf20Sopenharmony_ci geo->start = 0; 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct block_device_operations ubiblock_ops = { 2868c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2878c2ecf20Sopenharmony_ci .open = ubiblock_open, 2888c2ecf20Sopenharmony_ci .release = ubiblock_release, 2898c2ecf20Sopenharmony_ci .getgeo = ubiblock_getgeo, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void ubiblock_do_work(struct work_struct *work) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int ret; 2958c2ecf20Sopenharmony_ci struct ubiblock_pdu *pdu = container_of(work, struct ubiblock_pdu, work); 2968c2ecf20Sopenharmony_ci struct request *req = blk_mq_rq_from_pdu(pdu); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci blk_mq_start_request(req); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * It is safe to ignore the return value of blk_rq_map_sg() because 3028c2ecf20Sopenharmony_ci * the number of sg entries is limited to UBI_MAX_SG_COUNT 3038c2ecf20Sopenharmony_ci * and ubi_read_sg() will check that limit. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci blk_rq_map_sg(req->q, req, pdu->usgl.sg); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = ubiblock_read(pdu); 3088c2ecf20Sopenharmony_ci rq_flush_dcache_pages(req); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci blk_mq_end_request(req, errno_to_blk_status(ret)); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic blk_status_t ubiblock_queue_rq(struct blk_mq_hw_ctx *hctx, 3148c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct request *req = bd->rq; 3178c2ecf20Sopenharmony_ci struct ubiblock *dev = hctx->queue->queuedata; 3188c2ecf20Sopenharmony_ci struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci switch (req_op(req)) { 3218c2ecf20Sopenharmony_ci case REQ_OP_READ: 3228c2ecf20Sopenharmony_ci ubi_sgl_init(&pdu->usgl); 3238c2ecf20Sopenharmony_ci queue_work(dev->wq, &pdu->work); 3248c2ecf20Sopenharmony_ci return BLK_STS_OK; 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int ubiblock_init_request(struct blk_mq_tag_set *set, 3328c2ecf20Sopenharmony_ci struct request *req, unsigned int hctx_idx, 3338c2ecf20Sopenharmony_ci unsigned int numa_node) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci sg_init_table(pdu->usgl.sg, UBI_MAX_SG_COUNT); 3388c2ecf20Sopenharmony_ci INIT_WORK(&pdu->work, ubiblock_do_work); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic const struct blk_mq_ops ubiblock_mq_ops = { 3448c2ecf20Sopenharmony_ci .queue_rq = ubiblock_queue_rq, 3458c2ecf20Sopenharmony_ci .init_request = ubiblock_init_request, 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int calc_disk_capacity(struct ubi_volume_info *vi, u64 *disk_capacity) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci u64 size = vi->used_bytes >> 9; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (vi->used_bytes % 512) { 3538c2ecf20Sopenharmony_ci pr_warn("UBI: block: volume size is not a multiple of 512, " 3548c2ecf20Sopenharmony_ci "last %llu bytes are ignored!\n", 3558c2ecf20Sopenharmony_ci vi->used_bytes - (size << 9)); 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if ((sector_t)size != size) 3598c2ecf20Sopenharmony_ci return -EFBIG; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci *disk_capacity = size; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciint ubiblock_create(struct ubi_volume_info *vi) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct ubiblock *dev; 3698c2ecf20Sopenharmony_ci struct gendisk *gd; 3708c2ecf20Sopenharmony_ci u64 disk_capacity; 3718c2ecf20Sopenharmony_ci int ret; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ret = calc_disk_capacity(vi, &disk_capacity); 3748c2ecf20Sopenharmony_ci if (ret) { 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Check that the volume isn't already handled */ 3798c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 3808c2ecf20Sopenharmony_ci if (find_dev_nolock(vi->ubi_num, vi->vol_id)) { 3818c2ecf20Sopenharmony_ci ret = -EEXIST; 3828c2ecf20Sopenharmony_ci goto out_unlock; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct ubiblock), GFP_KERNEL); 3868c2ecf20Sopenharmony_ci if (!dev) { 3878c2ecf20Sopenharmony_ci ret = -ENOMEM; 3888c2ecf20Sopenharmony_ci goto out_unlock; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci mutex_init(&dev->dev_mutex); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dev->ubi_num = vi->ubi_num; 3948c2ecf20Sopenharmony_ci dev->vol_id = vi->vol_id; 3958c2ecf20Sopenharmony_ci dev->leb_size = vi->usable_leb_size; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* Initialize the gendisk of this ubiblock device */ 3988c2ecf20Sopenharmony_ci gd = alloc_disk(1); 3998c2ecf20Sopenharmony_ci if (!gd) { 4008c2ecf20Sopenharmony_ci pr_err("UBI: block: alloc_disk failed\n"); 4018c2ecf20Sopenharmony_ci ret = -ENODEV; 4028c2ecf20Sopenharmony_ci goto out_free_dev; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci gd->fops = &ubiblock_ops; 4068c2ecf20Sopenharmony_ci gd->major = ubiblock_major; 4078c2ecf20Sopenharmony_ci gd->first_minor = idr_alloc(&ubiblock_minor_idr, dev, 0, 0, GFP_KERNEL); 4088c2ecf20Sopenharmony_ci if (gd->first_minor < 0) { 4098c2ecf20Sopenharmony_ci dev_err(disk_to_dev(gd), 4108c2ecf20Sopenharmony_ci "block: dynamic minor allocation failed"); 4118c2ecf20Sopenharmony_ci ret = -ENODEV; 4128c2ecf20Sopenharmony_ci goto out_put_disk; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci gd->private_data = dev; 4158c2ecf20Sopenharmony_ci sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); 4168c2ecf20Sopenharmony_ci set_capacity(gd, disk_capacity); 4178c2ecf20Sopenharmony_ci dev->gd = gd; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci dev->tag_set.ops = &ubiblock_mq_ops; 4208c2ecf20Sopenharmony_ci dev->tag_set.queue_depth = 64; 4218c2ecf20Sopenharmony_ci dev->tag_set.numa_node = NUMA_NO_NODE; 4228c2ecf20Sopenharmony_ci dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; 4238c2ecf20Sopenharmony_ci dev->tag_set.cmd_size = sizeof(struct ubiblock_pdu); 4248c2ecf20Sopenharmony_ci dev->tag_set.driver_data = dev; 4258c2ecf20Sopenharmony_ci dev->tag_set.nr_hw_queues = 1; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ret = blk_mq_alloc_tag_set(&dev->tag_set); 4288c2ecf20Sopenharmony_ci if (ret) { 4298c2ecf20Sopenharmony_ci dev_err(disk_to_dev(dev->gd), "blk_mq_alloc_tag_set failed"); 4308c2ecf20Sopenharmony_ci goto out_remove_minor; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci dev->rq = blk_mq_init_queue(&dev->tag_set); 4348c2ecf20Sopenharmony_ci if (IS_ERR(dev->rq)) { 4358c2ecf20Sopenharmony_ci dev_err(disk_to_dev(gd), "blk_mq_init_queue failed"); 4368c2ecf20Sopenharmony_ci ret = PTR_ERR(dev->rq); 4378c2ecf20Sopenharmony_ci goto out_free_tags; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci blk_queue_max_segments(dev->rq, UBI_MAX_SG_COUNT); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci dev->rq->queuedata = dev; 4428c2ecf20Sopenharmony_ci dev->gd->queue = dev->rq; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Create one workqueue per volume (per registered block device). 4468c2ecf20Sopenharmony_ci * Rembember workqueues are cheap, they're not threads. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci dev->wq = alloc_workqueue("%s", 0, 0, gd->disk_name); 4498c2ecf20Sopenharmony_ci if (!dev->wq) { 4508c2ecf20Sopenharmony_ci ret = -ENOMEM; 4518c2ecf20Sopenharmony_ci goto out_free_queue; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci list_add_tail(&dev->list, &ubiblock_devices); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Must be the last step: anyone can call file ops from now on */ 4578c2ecf20Sopenharmony_ci add_disk(dev->gd); 4588c2ecf20Sopenharmony_ci dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", 4598c2ecf20Sopenharmony_ci dev->ubi_num, dev->vol_id, vi->name); 4608c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciout_free_queue: 4648c2ecf20Sopenharmony_ci blk_cleanup_queue(dev->rq); 4658c2ecf20Sopenharmony_ciout_free_tags: 4668c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&dev->tag_set); 4678c2ecf20Sopenharmony_ciout_remove_minor: 4688c2ecf20Sopenharmony_ci idr_remove(&ubiblock_minor_idr, gd->first_minor); 4698c2ecf20Sopenharmony_ciout_put_disk: 4708c2ecf20Sopenharmony_ci put_disk(dev->gd); 4718c2ecf20Sopenharmony_ciout_free_dev: 4728c2ecf20Sopenharmony_ci kfree(dev); 4738c2ecf20Sopenharmony_ciout_unlock: 4748c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void ubiblock_cleanup(struct ubiblock *dev) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci /* Stop new requests to arrive */ 4828c2ecf20Sopenharmony_ci del_gendisk(dev->gd); 4838c2ecf20Sopenharmony_ci /* Flush pending work */ 4848c2ecf20Sopenharmony_ci destroy_workqueue(dev->wq); 4858c2ecf20Sopenharmony_ci /* Finally destroy the blk queue */ 4868c2ecf20Sopenharmony_ci blk_cleanup_queue(dev->rq); 4878c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&dev->tag_set); 4888c2ecf20Sopenharmony_ci dev_info(disk_to_dev(dev->gd), "released"); 4898c2ecf20Sopenharmony_ci idr_remove(&ubiblock_minor_idr, dev->gd->first_minor); 4908c2ecf20Sopenharmony_ci put_disk(dev->gd); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciint ubiblock_remove(struct ubi_volume_info *vi) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct ubiblock *dev; 4968c2ecf20Sopenharmony_ci int ret; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 4998c2ecf20Sopenharmony_ci dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 5008c2ecf20Sopenharmony_ci if (!dev) { 5018c2ecf20Sopenharmony_ci ret = -ENODEV; 5028c2ecf20Sopenharmony_ci goto out_unlock; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Found a device, let's lock it so we can check if it's busy */ 5068c2ecf20Sopenharmony_ci mutex_lock(&dev->dev_mutex); 5078c2ecf20Sopenharmony_ci if (dev->refcnt > 0) { 5088c2ecf20Sopenharmony_ci ret = -EBUSY; 5098c2ecf20Sopenharmony_ci goto out_unlock_dev; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Remove from device list */ 5138c2ecf20Sopenharmony_ci list_del(&dev->list); 5148c2ecf20Sopenharmony_ci ubiblock_cleanup(dev); 5158c2ecf20Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 5168c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci kfree(dev); 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciout_unlock_dev: 5228c2ecf20Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 5238c2ecf20Sopenharmony_ciout_unlock: 5248c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic int ubiblock_resize(struct ubi_volume_info *vi) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct ubiblock *dev; 5318c2ecf20Sopenharmony_ci u64 disk_capacity; 5328c2ecf20Sopenharmony_ci int ret; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * Need to lock the device list until we stop using the device, 5368c2ecf20Sopenharmony_ci * otherwise the device struct might get released in 5378c2ecf20Sopenharmony_ci * 'ubiblock_remove()'. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 5408c2ecf20Sopenharmony_ci dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 5418c2ecf20Sopenharmony_ci if (!dev) { 5428c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 5438c2ecf20Sopenharmony_ci return -ENODEV; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ret = calc_disk_capacity(vi, &disk_capacity); 5478c2ecf20Sopenharmony_ci if (ret) { 5488c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 5498c2ecf20Sopenharmony_ci if (ret == -EFBIG) { 5508c2ecf20Sopenharmony_ci dev_warn(disk_to_dev(dev->gd), 5518c2ecf20Sopenharmony_ci "the volume is too big (%d LEBs), cannot resize", 5528c2ecf20Sopenharmony_ci vi->size); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci return ret; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci mutex_lock(&dev->dev_mutex); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (get_capacity(dev->gd) != disk_capacity) { 5608c2ecf20Sopenharmony_ci set_capacity(dev->gd, disk_capacity); 5618c2ecf20Sopenharmony_ci dev_info(disk_to_dev(dev->gd), "resized to %lld bytes", 5628c2ecf20Sopenharmony_ci vi->used_bytes); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 5658c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int ubiblock_notify(struct notifier_block *nb, 5708c2ecf20Sopenharmony_ci unsigned long notification_type, void *ns_ptr) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct ubi_notification *nt = ns_ptr; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci switch (notification_type) { 5758c2ecf20Sopenharmony_ci case UBI_VOLUME_ADDED: 5768c2ecf20Sopenharmony_ci /* 5778c2ecf20Sopenharmony_ci * We want to enforce explicit block device creation for 5788c2ecf20Sopenharmony_ci * volumes, so when a volume is added we do nothing. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci case UBI_VOLUME_REMOVED: 5828c2ecf20Sopenharmony_ci ubiblock_remove(&nt->vi); 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci case UBI_VOLUME_RESIZED: 5858c2ecf20Sopenharmony_ci ubiblock_resize(&nt->vi); 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci case UBI_VOLUME_UPDATED: 5888c2ecf20Sopenharmony_ci /* 5898c2ecf20Sopenharmony_ci * If the volume is static, a content update might mean the 5908c2ecf20Sopenharmony_ci * size (i.e. used_bytes) was also changed. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci if (nt->vi.vol_type == UBI_STATIC_VOLUME) 5938c2ecf20Sopenharmony_ci ubiblock_resize(&nt->vi); 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci default: 5968c2ecf20Sopenharmony_ci break; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci return NOTIFY_OK; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic struct notifier_block ubiblock_notifier = { 6028c2ecf20Sopenharmony_ci .notifier_call = ubiblock_notify, 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic struct ubi_volume_desc * __init 6068c2ecf20Sopenharmony_ciopen_volume_desc(const char *name, int ubi_num, int vol_id) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci if (ubi_num == -1) 6098c2ecf20Sopenharmony_ci /* No ubi num, name must be a vol device path */ 6108c2ecf20Sopenharmony_ci return ubi_open_volume_path(name, UBI_READONLY); 6118c2ecf20Sopenharmony_ci else if (vol_id == -1) 6128c2ecf20Sopenharmony_ci /* No vol_id, must be vol_name */ 6138c2ecf20Sopenharmony_ci return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); 6148c2ecf20Sopenharmony_ci else 6158c2ecf20Sopenharmony_ci return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic void __init ubiblock_create_from_param(void) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci int i, ret = 0; 6218c2ecf20Sopenharmony_ci struct ubiblock_param *p; 6228c2ecf20Sopenharmony_ci struct ubi_volume_desc *desc; 6238c2ecf20Sopenharmony_ci struct ubi_volume_info vi; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* 6268c2ecf20Sopenharmony_ci * If there is an error creating one of the ubiblocks, continue on to 6278c2ecf20Sopenharmony_ci * create the following ubiblocks. This helps in a circumstance where 6288c2ecf20Sopenharmony_ci * the kernel command-line specifies multiple block devices and some 6298c2ecf20Sopenharmony_ci * may be broken, but we still want the working ones to come up. 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci for (i = 0; i < ubiblock_devs; i++) { 6328c2ecf20Sopenharmony_ci p = &ubiblock_param[i]; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); 6358c2ecf20Sopenharmony_ci if (IS_ERR(desc)) { 6368c2ecf20Sopenharmony_ci pr_err( 6378c2ecf20Sopenharmony_ci "UBI: block: can't open volume on ubi%d_%d, err=%ld\n", 6388c2ecf20Sopenharmony_ci p->ubi_num, p->vol_id, PTR_ERR(desc)); 6398c2ecf20Sopenharmony_ci continue; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci ubi_get_volume_info(desc, &vi); 6438c2ecf20Sopenharmony_ci ubi_close_volume(desc); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci ret = ubiblock_create(&vi); 6468c2ecf20Sopenharmony_ci if (ret) { 6478c2ecf20Sopenharmony_ci pr_err( 6488c2ecf20Sopenharmony_ci "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", 6498c2ecf20Sopenharmony_ci vi.name, p->ubi_num, p->vol_id, ret); 6508c2ecf20Sopenharmony_ci continue; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void ubiblock_remove_all(void) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct ubiblock *next; 6588c2ecf20Sopenharmony_ci struct ubiblock *dev; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 6618c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, next, &ubiblock_devices, list) { 6628c2ecf20Sopenharmony_ci /* The module is being forcefully removed */ 6638c2ecf20Sopenharmony_ci WARN_ON(dev->desc); 6648c2ecf20Sopenharmony_ci /* Remove from device list */ 6658c2ecf20Sopenharmony_ci list_del(&dev->list); 6668c2ecf20Sopenharmony_ci ubiblock_cleanup(dev); 6678c2ecf20Sopenharmony_ci kfree(dev); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ciint __init ubiblock_init(void) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci int ret; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci ubiblock_major = register_blkdev(0, "ubiblock"); 6778c2ecf20Sopenharmony_ci if (ubiblock_major < 0) 6788c2ecf20Sopenharmony_ci return ubiblock_major; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* 6818c2ecf20Sopenharmony_ci * Attach block devices from 'block=' module param. 6828c2ecf20Sopenharmony_ci * Even if one block device in the param list fails to come up, 6838c2ecf20Sopenharmony_ci * still allow the module to load and leave any others up. 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_ci ubiblock_create_from_param(); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* 6888c2ecf20Sopenharmony_ci * Block devices are only created upon user requests, so we ignore 6898c2ecf20Sopenharmony_ci * existing volumes. 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_ci ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); 6928c2ecf20Sopenharmony_ci if (ret) 6938c2ecf20Sopenharmony_ci goto err_unreg; 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cierr_unreg: 6978c2ecf20Sopenharmony_ci unregister_blkdev(ubiblock_major, "ubiblock"); 6988c2ecf20Sopenharmony_ci ubiblock_remove_all(); 6998c2ecf20Sopenharmony_ci return ret; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_civoid __exit ubiblock_exit(void) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci ubi_unregister_volume_notifier(&ubiblock_notifier); 7058c2ecf20Sopenharmony_ci ubiblock_remove_all(); 7068c2ecf20Sopenharmony_ci unregister_blkdev(ubiblock_major, "ubiblock"); 7078c2ecf20Sopenharmony_ci} 708