162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014 Ezequiel Garcia 462306a36Sopenharmony_ci * Copyright (c) 2011 Free Electrons 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Driver parameter handling strongly based on drivers/mtd/ubi/build.c 762306a36Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006 862306a36Sopenharmony_ci * Copyright (c) Nokia Corporation, 2007 962306a36Sopenharmony_ci * Authors: Artem Bityutskiy, Frank Haverkamp 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * Read-only block devices on top of UBI volumes 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * A simple implementation to allow a block device to be layered on top of a 1662306a36Sopenharmony_ci * UBI volume. The implementation is provided by creating a static 1-to-1 1762306a36Sopenharmony_ci * mapping between the block device and the UBI volume. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * The addressed byte is obtained from the addressed block sector, which is 2062306a36Sopenharmony_ci * mapped linearly into the corresponding LEB: 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * LEB number = addressed byte / LEB size 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * This feature is compiled in the UBI core, and adds a 'block' parameter 2562306a36Sopenharmony_ci * to allow early creation of block devices on top of UBI volumes. Runtime 2662306a36Sopenharmony_ci * block creation/removal for UBI volumes is provided through two UBI ioctls: 2762306a36Sopenharmony_ci * UBI_IOCVOLCRBLK and UBI_IOCVOLRMBLK. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/init.h> 3262306a36Sopenharmony_ci#include <linux/err.h> 3362306a36Sopenharmony_ci#include <linux/kernel.h> 3462306a36Sopenharmony_ci#include <linux/list.h> 3562306a36Sopenharmony_ci#include <linux/mutex.h> 3662306a36Sopenharmony_ci#include <linux/slab.h> 3762306a36Sopenharmony_ci#include <linux/mtd/ubi.h> 3862306a36Sopenharmony_ci#include <linux/blkdev.h> 3962306a36Sopenharmony_ci#include <linux/blk-mq.h> 4062306a36Sopenharmony_ci#include <linux/hdreg.h> 4162306a36Sopenharmony_ci#include <linux/scatterlist.h> 4262306a36Sopenharmony_ci#include <linux/idr.h> 4362306a36Sopenharmony_ci#include <asm/div64.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "ubi-media.h" 4662306a36Sopenharmony_ci#include "ubi.h" 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Maximum number of supported devices */ 4962306a36Sopenharmony_ci#define UBIBLOCK_MAX_DEVICES 32 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Maximum length of the 'block=' parameter */ 5262306a36Sopenharmony_ci#define UBIBLOCK_PARAM_LEN 63 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Maximum number of comma-separated items in the 'block=' parameter */ 5562306a36Sopenharmony_ci#define UBIBLOCK_PARAM_COUNT 2 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct ubiblock_param { 5862306a36Sopenharmony_ci int ubi_num; 5962306a36Sopenharmony_ci int vol_id; 6062306a36Sopenharmony_ci char name[UBIBLOCK_PARAM_LEN+1]; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct ubiblock_pdu { 6462306a36Sopenharmony_ci struct ubi_sgl usgl; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Numbers of elements set in the @ubiblock_param array */ 6862306a36Sopenharmony_cistatic int ubiblock_devs __initdata; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* MTD devices specification parameters */ 7162306a36Sopenharmony_cistatic struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct ubiblock { 7462306a36Sopenharmony_ci struct ubi_volume_desc *desc; 7562306a36Sopenharmony_ci int ubi_num; 7662306a36Sopenharmony_ci int vol_id; 7762306a36Sopenharmony_ci int refcnt; 7862306a36Sopenharmony_ci int leb_size; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci struct gendisk *gd; 8162306a36Sopenharmony_ci struct request_queue *rq; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci struct mutex dev_mutex; 8462306a36Sopenharmony_ci struct list_head list; 8562306a36Sopenharmony_ci struct blk_mq_tag_set tag_set; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Linked list of all ubiblock instances */ 8962306a36Sopenharmony_cistatic LIST_HEAD(ubiblock_devices); 9062306a36Sopenharmony_cistatic DEFINE_IDR(ubiblock_minor_idr); 9162306a36Sopenharmony_ci/* Protects ubiblock_devices and ubiblock_minor_idr */ 9262306a36Sopenharmony_cistatic DEFINE_MUTEX(devices_mutex); 9362306a36Sopenharmony_cistatic int ubiblock_major; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int __init ubiblock_set_param(const char *val, 9662306a36Sopenharmony_ci const struct kernel_param *kp) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int i, ret; 9962306a36Sopenharmony_ci size_t len; 10062306a36Sopenharmony_ci struct ubiblock_param *param; 10162306a36Sopenharmony_ci char buf[UBIBLOCK_PARAM_LEN]; 10262306a36Sopenharmony_ci char *pbuf = &buf[0]; 10362306a36Sopenharmony_ci char *tokens[UBIBLOCK_PARAM_COUNT]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!val) 10662306a36Sopenharmony_ci return -EINVAL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci len = strnlen(val, UBIBLOCK_PARAM_LEN); 10962306a36Sopenharmony_ci if (len == 0) { 11062306a36Sopenharmony_ci pr_warn("UBI: block: empty 'block=' parameter - ignored\n"); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (len == UBIBLOCK_PARAM_LEN) { 11562306a36Sopenharmony_ci pr_err("UBI: block: parameter \"%s\" is too long, max. is %d\n", 11662306a36Sopenharmony_ci val, UBIBLOCK_PARAM_LEN); 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci strcpy(buf, val); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Get rid of the final newline */ 12362306a36Sopenharmony_ci if (buf[len - 1] == '\n') 12462306a36Sopenharmony_ci buf[len - 1] = '\0'; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < UBIBLOCK_PARAM_COUNT; i++) 12762306a36Sopenharmony_ci tokens[i] = strsep(&pbuf, ","); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci param = &ubiblock_param[ubiblock_devs]; 13062306a36Sopenharmony_ci if (tokens[1]) { 13162306a36Sopenharmony_ci /* Two parameters: can be 'ubi, vol_id' or 'ubi, vol_name' */ 13262306a36Sopenharmony_ci ret = kstrtoint(tokens[0], 10, ¶m->ubi_num); 13362306a36Sopenharmony_ci if (ret < 0) 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Second param can be a number or a name */ 13762306a36Sopenharmony_ci ret = kstrtoint(tokens[1], 10, ¶m->vol_id); 13862306a36Sopenharmony_ci if (ret < 0) { 13962306a36Sopenharmony_ci param->vol_id = -1; 14062306a36Sopenharmony_ci strcpy(param->name, tokens[1]); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci } else { 14462306a36Sopenharmony_ci /* One parameter: must be device path */ 14562306a36Sopenharmony_ci strcpy(param->name, tokens[0]); 14662306a36Sopenharmony_ci param->ubi_num = -1; 14762306a36Sopenharmony_ci param->vol_id = -1; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ubiblock_devs++; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const struct kernel_param_ops ubiblock_param_ops = { 15662306a36Sopenharmony_ci .set = ubiblock_set_param, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_cimodule_param_cb(block, &ubiblock_param_ops, NULL, 0); 15962306a36Sopenharmony_ciMODULE_PARM_DESC(block, "Attach block devices to UBI volumes. Parameter format: block=<path|dev,num|dev,name>.\n" 16062306a36Sopenharmony_ci "Multiple \"block\" parameters may be specified.\n" 16162306a36Sopenharmony_ci "UBI volumes may be specified by their number, name, or path to the device node.\n" 16262306a36Sopenharmony_ci "Examples\n" 16362306a36Sopenharmony_ci "Using the UBI volume path:\n" 16462306a36Sopenharmony_ci "ubi.block=/dev/ubi0_0\n" 16562306a36Sopenharmony_ci "Using the UBI device, and the volume name:\n" 16662306a36Sopenharmony_ci "ubi.block=0,rootfs\n" 16762306a36Sopenharmony_ci "Using both UBI device number and UBI volume number:\n" 16862306a36Sopenharmony_ci "ubi.block=0,0\n"); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic struct ubiblock *find_dev_nolock(int ubi_num, int vol_id) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct ubiblock *dev; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci list_for_each_entry(dev, &ubiblock_devices, list) 17562306a36Sopenharmony_ci if (dev->ubi_num == ubi_num && dev->vol_id == vol_id) 17662306a36Sopenharmony_ci return dev; 17762306a36Sopenharmony_ci return NULL; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic blk_status_t ubiblock_read(struct request *req) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 18362306a36Sopenharmony_ci struct ubiblock *dev = req->q->queuedata; 18462306a36Sopenharmony_ci u64 pos = blk_rq_pos(req) << 9; 18562306a36Sopenharmony_ci int to_read = blk_rq_bytes(req); 18662306a36Sopenharmony_ci int bytes_left = to_read; 18762306a36Sopenharmony_ci /* Get LEB:offset address to read from */ 18862306a36Sopenharmony_ci int offset = do_div(pos, dev->leb_size); 18962306a36Sopenharmony_ci int leb = pos; 19062306a36Sopenharmony_ci struct req_iterator iter; 19162306a36Sopenharmony_ci struct bio_vec bvec; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci blk_mq_start_request(req); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * It is safe to ignore the return value of blk_rq_map_sg() because 19862306a36Sopenharmony_ci * the number of sg entries is limited to UBI_MAX_SG_COUNT 19962306a36Sopenharmony_ci * and ubi_read_sg() will check that limit. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci ubi_sgl_init(&pdu->usgl); 20262306a36Sopenharmony_ci blk_rq_map_sg(req->q, req, pdu->usgl.sg); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci while (bytes_left) { 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * We can only read one LEB at a time. Therefore if the read 20762306a36Sopenharmony_ci * length is larger than one LEB size, we split the operation. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci if (offset + to_read > dev->leb_size) 21062306a36Sopenharmony_ci to_read = dev->leb_size - offset; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ret = ubi_read_sg(dev->desc, leb, &pdu->usgl, offset, to_read); 21362306a36Sopenharmony_ci if (ret < 0) 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci bytes_left -= to_read; 21762306a36Sopenharmony_ci to_read = bytes_left; 21862306a36Sopenharmony_ci leb += 1; 21962306a36Sopenharmony_ci offset = 0; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci rq_for_each_segment(bvec, req, iter) 22362306a36Sopenharmony_ci flush_dcache_page(bvec.bv_page); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci blk_mq_end_request(req, errno_to_blk_status(ret)); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return BLK_STS_OK; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int ubiblock_open(struct gendisk *disk, blk_mode_t mode) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct ubiblock *dev = disk->private_data; 23362306a36Sopenharmony_ci int ret; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mutex_lock(&dev->dev_mutex); 23662306a36Sopenharmony_ci if (dev->refcnt > 0) { 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * The volume is already open, just increase the reference 23962306a36Sopenharmony_ci * counter. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci goto out_done; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * We want users to be aware they should only mount us as read-only. 24662306a36Sopenharmony_ci * It's just a paranoid check, as write requests will get rejected 24762306a36Sopenharmony_ci * in any case. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci if (mode & BLK_OPEN_WRITE) { 25062306a36Sopenharmony_ci ret = -EROFS; 25162306a36Sopenharmony_ci goto out_unlock; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY); 25462306a36Sopenharmony_ci if (IS_ERR(dev->desc)) { 25562306a36Sopenharmony_ci dev_err(disk_to_dev(dev->gd), "failed to open ubi volume %d_%d", 25662306a36Sopenharmony_ci dev->ubi_num, dev->vol_id); 25762306a36Sopenharmony_ci ret = PTR_ERR(dev->desc); 25862306a36Sopenharmony_ci dev->desc = NULL; 25962306a36Sopenharmony_ci goto out_unlock; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ciout_done: 26362306a36Sopenharmony_ci dev->refcnt++; 26462306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciout_unlock: 26862306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void ubiblock_release(struct gendisk *gd) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct ubiblock *dev = gd->private_data; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci mutex_lock(&dev->dev_mutex); 27762306a36Sopenharmony_ci dev->refcnt--; 27862306a36Sopenharmony_ci if (dev->refcnt == 0) { 27962306a36Sopenharmony_ci ubi_close_volume(dev->desc); 28062306a36Sopenharmony_ci dev->desc = NULL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int ubiblock_getgeo(struct block_device *bdev, struct hd_geometry *geo) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci /* Some tools might require this information */ 28862306a36Sopenharmony_ci geo->heads = 1; 28962306a36Sopenharmony_ci geo->cylinders = 1; 29062306a36Sopenharmony_ci geo->sectors = get_capacity(bdev->bd_disk); 29162306a36Sopenharmony_ci geo->start = 0; 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct block_device_operations ubiblock_ops = { 29662306a36Sopenharmony_ci .owner = THIS_MODULE, 29762306a36Sopenharmony_ci .open = ubiblock_open, 29862306a36Sopenharmony_ci .release = ubiblock_release, 29962306a36Sopenharmony_ci .getgeo = ubiblock_getgeo, 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic blk_status_t ubiblock_queue_rq(struct blk_mq_hw_ctx *hctx, 30362306a36Sopenharmony_ci const struct blk_mq_queue_data *bd) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci switch (req_op(bd->rq)) { 30662306a36Sopenharmony_ci case REQ_OP_READ: 30762306a36Sopenharmony_ci return ubiblock_read(bd->rq); 30862306a36Sopenharmony_ci default: 30962306a36Sopenharmony_ci return BLK_STS_IOERR; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int ubiblock_init_request(struct blk_mq_tag_set *set, 31462306a36Sopenharmony_ci struct request *req, unsigned int hctx_idx, 31562306a36Sopenharmony_ci unsigned int numa_node) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci sg_init_table(pdu->usgl.sg, UBI_MAX_SG_COUNT); 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct blk_mq_ops ubiblock_mq_ops = { 32462306a36Sopenharmony_ci .queue_rq = ubiblock_queue_rq, 32562306a36Sopenharmony_ci .init_request = ubiblock_init_request, 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int calc_disk_capacity(struct ubi_volume_info *vi, u64 *disk_capacity) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci u64 size = vi->used_bytes >> 9; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (vi->used_bytes % 512) { 33362306a36Sopenharmony_ci if (vi->vol_type == UBI_DYNAMIC_VOLUME) 33462306a36Sopenharmony_ci pr_warn("UBI: block: volume size is not a multiple of 512, last %llu bytes are ignored!\n", 33562306a36Sopenharmony_ci vi->used_bytes - (size << 9)); 33662306a36Sopenharmony_ci else 33762306a36Sopenharmony_ci pr_info("UBI: block: volume size is not a multiple of 512, last %llu bytes are ignored!\n", 33862306a36Sopenharmony_ci vi->used_bytes - (size << 9)); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if ((sector_t)size != size) 34262306a36Sopenharmony_ci return -EFBIG; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci *disk_capacity = size; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciint ubiblock_create(struct ubi_volume_info *vi) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct ubiblock *dev; 35262306a36Sopenharmony_ci struct gendisk *gd; 35362306a36Sopenharmony_ci u64 disk_capacity; 35462306a36Sopenharmony_ci int ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = calc_disk_capacity(vi, &disk_capacity); 35762306a36Sopenharmony_ci if (ret) { 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Check that the volume isn't already handled */ 36262306a36Sopenharmony_ci mutex_lock(&devices_mutex); 36362306a36Sopenharmony_ci if (find_dev_nolock(vi->ubi_num, vi->vol_id)) { 36462306a36Sopenharmony_ci ret = -EEXIST; 36562306a36Sopenharmony_ci goto out_unlock; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci dev = kzalloc(sizeof(struct ubiblock), GFP_KERNEL); 36962306a36Sopenharmony_ci if (!dev) { 37062306a36Sopenharmony_ci ret = -ENOMEM; 37162306a36Sopenharmony_ci goto out_unlock; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci mutex_init(&dev->dev_mutex); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci dev->ubi_num = vi->ubi_num; 37762306a36Sopenharmony_ci dev->vol_id = vi->vol_id; 37862306a36Sopenharmony_ci dev->leb_size = vi->usable_leb_size; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci dev->tag_set.ops = &ubiblock_mq_ops; 38162306a36Sopenharmony_ci dev->tag_set.queue_depth = 64; 38262306a36Sopenharmony_ci dev->tag_set.numa_node = NUMA_NO_NODE; 38362306a36Sopenharmony_ci dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING; 38462306a36Sopenharmony_ci dev->tag_set.cmd_size = sizeof(struct ubiblock_pdu); 38562306a36Sopenharmony_ci dev->tag_set.driver_data = dev; 38662306a36Sopenharmony_ci dev->tag_set.nr_hw_queues = 1; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = blk_mq_alloc_tag_set(&dev->tag_set); 38962306a36Sopenharmony_ci if (ret) { 39062306a36Sopenharmony_ci dev_err(disk_to_dev(dev->gd), "blk_mq_alloc_tag_set failed"); 39162306a36Sopenharmony_ci goto out_free_dev; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Initialize the gendisk of this ubiblock device */ 39662306a36Sopenharmony_ci gd = blk_mq_alloc_disk(&dev->tag_set, dev); 39762306a36Sopenharmony_ci if (IS_ERR(gd)) { 39862306a36Sopenharmony_ci ret = PTR_ERR(gd); 39962306a36Sopenharmony_ci goto out_free_tags; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci gd->fops = &ubiblock_ops; 40362306a36Sopenharmony_ci gd->major = ubiblock_major; 40462306a36Sopenharmony_ci gd->minors = 1; 40562306a36Sopenharmony_ci gd->first_minor = idr_alloc(&ubiblock_minor_idr, dev, 0, 0, GFP_KERNEL); 40662306a36Sopenharmony_ci if (gd->first_minor < 0) { 40762306a36Sopenharmony_ci dev_err(disk_to_dev(gd), 40862306a36Sopenharmony_ci "block: dynamic minor allocation failed"); 40962306a36Sopenharmony_ci ret = -ENODEV; 41062306a36Sopenharmony_ci goto out_cleanup_disk; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci gd->flags |= GENHD_FL_NO_PART; 41362306a36Sopenharmony_ci gd->private_data = dev; 41462306a36Sopenharmony_ci sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); 41562306a36Sopenharmony_ci set_capacity(gd, disk_capacity); 41662306a36Sopenharmony_ci dev->gd = gd; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci dev->rq = gd->queue; 41962306a36Sopenharmony_ci blk_queue_max_segments(dev->rq, UBI_MAX_SG_COUNT); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci list_add_tail(&dev->list, &ubiblock_devices); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Must be the last step: anyone can call file ops from now on */ 42462306a36Sopenharmony_ci ret = device_add_disk(vi->dev, dev->gd, NULL); 42562306a36Sopenharmony_ci if (ret) 42662306a36Sopenharmony_ci goto out_remove_minor; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", 42962306a36Sopenharmony_ci dev->ubi_num, dev->vol_id, vi->name); 43062306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciout_remove_minor: 43462306a36Sopenharmony_ci list_del(&dev->list); 43562306a36Sopenharmony_ci idr_remove(&ubiblock_minor_idr, gd->first_minor); 43662306a36Sopenharmony_ciout_cleanup_disk: 43762306a36Sopenharmony_ci put_disk(dev->gd); 43862306a36Sopenharmony_ciout_free_tags: 43962306a36Sopenharmony_ci blk_mq_free_tag_set(&dev->tag_set); 44062306a36Sopenharmony_ciout_free_dev: 44162306a36Sopenharmony_ci kfree(dev); 44262306a36Sopenharmony_ciout_unlock: 44362306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return ret; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void ubiblock_cleanup(struct ubiblock *dev) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci /* Stop new requests to arrive */ 45162306a36Sopenharmony_ci del_gendisk(dev->gd); 45262306a36Sopenharmony_ci /* Finally destroy the blk queue */ 45362306a36Sopenharmony_ci dev_info(disk_to_dev(dev->gd), "released"); 45462306a36Sopenharmony_ci put_disk(dev->gd); 45562306a36Sopenharmony_ci blk_mq_free_tag_set(&dev->tag_set); 45662306a36Sopenharmony_ci idr_remove(&ubiblock_minor_idr, dev->gd->first_minor); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ciint ubiblock_remove(struct ubi_volume_info *vi) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct ubiblock *dev; 46262306a36Sopenharmony_ci int ret; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci mutex_lock(&devices_mutex); 46562306a36Sopenharmony_ci dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 46662306a36Sopenharmony_ci if (!dev) { 46762306a36Sopenharmony_ci ret = -ENODEV; 46862306a36Sopenharmony_ci goto out_unlock; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Found a device, let's lock it so we can check if it's busy */ 47262306a36Sopenharmony_ci mutex_lock(&dev->dev_mutex); 47362306a36Sopenharmony_ci if (dev->refcnt > 0) { 47462306a36Sopenharmony_ci ret = -EBUSY; 47562306a36Sopenharmony_ci goto out_unlock_dev; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Remove from device list */ 47962306a36Sopenharmony_ci list_del(&dev->list); 48062306a36Sopenharmony_ci ubiblock_cleanup(dev); 48162306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 48262306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci kfree(dev); 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ciout_unlock_dev: 48862306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 48962306a36Sopenharmony_ciout_unlock: 49062306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 49162306a36Sopenharmony_ci return ret; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int ubiblock_resize(struct ubi_volume_info *vi) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct ubiblock *dev; 49762306a36Sopenharmony_ci u64 disk_capacity; 49862306a36Sopenharmony_ci int ret; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* 50162306a36Sopenharmony_ci * Need to lock the device list until we stop using the device, 50262306a36Sopenharmony_ci * otherwise the device struct might get released in 50362306a36Sopenharmony_ci * 'ubiblock_remove()'. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci mutex_lock(&devices_mutex); 50662306a36Sopenharmony_ci dev = find_dev_nolock(vi->ubi_num, vi->vol_id); 50762306a36Sopenharmony_ci if (!dev) { 50862306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 50962306a36Sopenharmony_ci return -ENODEV; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = calc_disk_capacity(vi, &disk_capacity); 51362306a36Sopenharmony_ci if (ret) { 51462306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 51562306a36Sopenharmony_ci if (ret == -EFBIG) { 51662306a36Sopenharmony_ci dev_warn(disk_to_dev(dev->gd), 51762306a36Sopenharmony_ci "the volume is too big (%d LEBs), cannot resize", 51862306a36Sopenharmony_ci vi->size); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci return ret; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mutex_lock(&dev->dev_mutex); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (get_capacity(dev->gd) != disk_capacity) { 52662306a36Sopenharmony_ci set_capacity(dev->gd, disk_capacity); 52762306a36Sopenharmony_ci dev_info(disk_to_dev(dev->gd), "resized to %lld bytes", 52862306a36Sopenharmony_ci vi->used_bytes); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 53162306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int ubiblock_notify(struct notifier_block *nb, 53662306a36Sopenharmony_ci unsigned long notification_type, void *ns_ptr) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct ubi_notification *nt = ns_ptr; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci switch (notification_type) { 54162306a36Sopenharmony_ci case UBI_VOLUME_ADDED: 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * We want to enforce explicit block device creation for 54462306a36Sopenharmony_ci * volumes, so when a volume is added we do nothing. 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case UBI_VOLUME_REMOVED: 54862306a36Sopenharmony_ci ubiblock_remove(&nt->vi); 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case UBI_VOLUME_RESIZED: 55162306a36Sopenharmony_ci ubiblock_resize(&nt->vi); 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case UBI_VOLUME_UPDATED: 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci * If the volume is static, a content update might mean the 55662306a36Sopenharmony_ci * size (i.e. used_bytes) was also changed. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci if (nt->vi.vol_type == UBI_STATIC_VOLUME) 55962306a36Sopenharmony_ci ubiblock_resize(&nt->vi); 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci default: 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci return NOTIFY_OK; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic struct notifier_block ubiblock_notifier = { 56862306a36Sopenharmony_ci .notifier_call = ubiblock_notify, 56962306a36Sopenharmony_ci}; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic struct ubi_volume_desc * __init 57262306a36Sopenharmony_ciopen_volume_desc(const char *name, int ubi_num, int vol_id) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci if (ubi_num == -1) 57562306a36Sopenharmony_ci /* No ubi num, name must be a vol device path */ 57662306a36Sopenharmony_ci return ubi_open_volume_path(name, UBI_READONLY); 57762306a36Sopenharmony_ci else if (vol_id == -1) 57862306a36Sopenharmony_ci /* No vol_id, must be vol_name */ 57962306a36Sopenharmony_ci return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void __init ubiblock_create_from_param(void) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci int i, ret = 0; 58762306a36Sopenharmony_ci struct ubiblock_param *p; 58862306a36Sopenharmony_ci struct ubi_volume_desc *desc; 58962306a36Sopenharmony_ci struct ubi_volume_info vi; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * If there is an error creating one of the ubiblocks, continue on to 59362306a36Sopenharmony_ci * create the following ubiblocks. This helps in a circumstance where 59462306a36Sopenharmony_ci * the kernel command-line specifies multiple block devices and some 59562306a36Sopenharmony_ci * may be broken, but we still want the working ones to come up. 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci for (i = 0; i < ubiblock_devs; i++) { 59862306a36Sopenharmony_ci p = &ubiblock_param[i]; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); 60162306a36Sopenharmony_ci if (IS_ERR(desc)) { 60262306a36Sopenharmony_ci pr_err( 60362306a36Sopenharmony_ci "UBI: block: can't open volume on ubi%d_%d, err=%ld\n", 60462306a36Sopenharmony_ci p->ubi_num, p->vol_id, PTR_ERR(desc)); 60562306a36Sopenharmony_ci continue; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ubi_get_volume_info(desc, &vi); 60962306a36Sopenharmony_ci ubi_close_volume(desc); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = ubiblock_create(&vi); 61262306a36Sopenharmony_ci if (ret) { 61362306a36Sopenharmony_ci pr_err( 61462306a36Sopenharmony_ci "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", 61562306a36Sopenharmony_ci vi.name, p->ubi_num, p->vol_id, ret); 61662306a36Sopenharmony_ci continue; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void ubiblock_remove_all(void) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct ubiblock *next; 62462306a36Sopenharmony_ci struct ubiblock *dev; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci mutex_lock(&devices_mutex); 62762306a36Sopenharmony_ci list_for_each_entry_safe(dev, next, &ubiblock_devices, list) { 62862306a36Sopenharmony_ci /* The module is being forcefully removed */ 62962306a36Sopenharmony_ci WARN_ON(dev->desc); 63062306a36Sopenharmony_ci /* Remove from device list */ 63162306a36Sopenharmony_ci list_del(&dev->list); 63262306a36Sopenharmony_ci ubiblock_cleanup(dev); 63362306a36Sopenharmony_ci kfree(dev); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ciint __init ubiblock_init(void) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci int ret; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ubiblock_major = register_blkdev(0, "ubiblock"); 64362306a36Sopenharmony_ci if (ubiblock_major < 0) 64462306a36Sopenharmony_ci return ubiblock_major; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * Attach block devices from 'block=' module param. 64862306a36Sopenharmony_ci * Even if one block device in the param list fails to come up, 64962306a36Sopenharmony_ci * still allow the module to load and leave any others up. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci ubiblock_create_from_param(); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* 65462306a36Sopenharmony_ci * Block devices are only created upon user requests, so we ignore 65562306a36Sopenharmony_ci * existing volumes. 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); 65862306a36Sopenharmony_ci if (ret) 65962306a36Sopenharmony_ci goto err_unreg; 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cierr_unreg: 66362306a36Sopenharmony_ci unregister_blkdev(ubiblock_major, "ubiblock"); 66462306a36Sopenharmony_ci ubiblock_remove_all(); 66562306a36Sopenharmony_ci return ret; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_civoid __exit ubiblock_exit(void) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci ubi_unregister_volume_notifier(&ubiblock_notifier); 67162306a36Sopenharmony_ci ubiblock_remove_all(); 67262306a36Sopenharmony_ci unregister_blkdev(ubiblock_major, "ubiblock"); 67362306a36Sopenharmony_ci} 674