678 lines · 15.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2014 Ezequiel Garcia * Copyright (c) 2011 Free Electrons * * Driver parameter handling strongly based on drivers/mtd/ubi/build.c *   Copyright (c) International Business Machines Corp., 2006 *   Copyright (c) Nokia Corporation, 2007 *   Authors: Artem Bityutskiy, Frank Haverkamp */ /* * Read-only block devices on top of UBI volumes * * A simple implementation to allow a block device to be layered on top of a * UBI volume. The implementation is provided by creating a static 1-to-1 * mapping between the block device and the UBI volume. * * The addressed byte is obtained from the addressed block sector, which is * mapped linearly into the corresponding LEB: * *   LEB number = addressed byte / LEB size * * This feature is compiled in the UBI core, and adds a 'block' parameter * to allow early creation of block devices on top of UBI volumes. Runtime * block creation/removal for UBI volumes is provided through two UBI ioctls: * UBI_IOCVOLCRBLK and UBI_IOCVOLRMBLK. */ #include <linux/module.h>#include <linux/init.h>#include <linux/err.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/mtd/ubi.h>#include <linux/blkdev.h>#include <linux/blk-mq.h>#include <linux/hdreg.h>#include <linux/scatterlist.h>#include <linux/idr.h>#include <asm/div64.h> #include "ubi-media.h"#include "ubi.h" /* Maximum number of supported devices */#define UBIBLOCK_MAX_DEVICES 32 /* Maximum length of the 'block=' parameter */#define UBIBLOCK_PARAM_LEN 63 /* Maximum number of comma-separated items in the 'block=' parameter */#define UBIBLOCK_PARAM_COUNT 2 struct ubiblock_param {	int ubi_num;	int vol_id;	char name[UBIBLOCK_PARAM_LEN+1];}; struct ubiblock_pdu {	struct ubi_sgl usgl;}; /* Numbers of elements set in the @ubiblock_param array */static int ubiblock_devs; /* MTD devices specification parameters */static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES]; struct ubiblock {	struct ubi_volume_desc *desc;	int ubi_num;	int vol_id;	int refcnt;	int leb_size; 	struct gendisk *gd;	struct request_queue *rq; 	struct mutex dev_mutex;	struct list_head list;	struct blk_mq_tag_set tag_set;}; /* Linked list of all ubiblock instances */static LIST_HEAD(ubiblock_devices);static DEFINE_IDR(ubiblock_minor_idr);/* Protects ubiblock_devices and ubiblock_minor_idr */static DEFINE_MUTEX(devices_mutex);static int ubiblock_major; static int __init ubiblock_set_param(const char *val,				     const struct kernel_param *kp){	int i, ret;	size_t len;	struct ubiblock_param *param;	char buf[UBIBLOCK_PARAM_LEN];	char *pbuf = &buf[0];	char *tokens[UBIBLOCK_PARAM_COUNT]; 	if (!val)		return -EINVAL; 	len = strnlen(val, UBIBLOCK_PARAM_LEN);	if (len == 0) {		pr_warn("UBI: block: empty 'block=' parameter - ignored\n");		return 0;	} 	if (len == UBIBLOCK_PARAM_LEN) {		pr_err("UBI: block: parameter \"%s\" is too long, max. is %d\n",		       val, UBIBLOCK_PARAM_LEN);		return -EINVAL;	} 	strcpy(buf, val); 	/* Get rid of the final newline */	if (buf[len - 1] == '\n')		buf[len - 1] = '\0'; 	for (i = 0; i < UBIBLOCK_PARAM_COUNT; i++)		tokens[i] = strsep(&pbuf, ","); 	param = &ubiblock_param[ubiblock_devs];	if (tokens[1]) {		/* Two parameters: can be 'ubi, vol_id' or 'ubi, vol_name' */		ret = kstrtoint(tokens[0], 10, &param->ubi_num);		if (ret < 0)			return -EINVAL; 		/* Second param can be a number or a name */		ret = kstrtoint(tokens[1], 10, &param->vol_id);		if (ret < 0) {			param->vol_id = -1;			strcpy(param->name, tokens[1]);		} 	} else {		/* One parameter: must be device path */		strcpy(param->name, tokens[0]);		param->ubi_num = -1;		param->vol_id = -1;	} 	ubiblock_devs++; 	return 0;} static const struct kernel_param_ops ubiblock_param_ops = {	.set    = ubiblock_set_param,};module_param_cb(block, &ubiblock_param_ops, NULL, 0);MODULE_PARM_DESC(block, "Attach block devices to UBI volumes. Parameter format: block=<path|dev,num|dev,name>.\n"			"Multiple \"block\" parameters may be specified.\n"			"UBI volumes may be specified by their number, name, or path to the device node.\n"			"Examples\n"			"Using the UBI volume path:\n"			"ubi.block=/dev/ubi0_0\n"			"Using the UBI device, and the volume name:\n"			"ubi.block=0,rootfs\n"			"Using both UBI device number and UBI volume number:\n"			"ubi.block=0,0\n"); static struct ubiblock *find_dev_nolock(int ubi_num, int vol_id){	struct ubiblock *dev; 	list_for_each_entry(dev, &ubiblock_devices, list)		if (dev->ubi_num == ubi_num && dev->vol_id == vol_id)			return dev;	return NULL;} static blk_status_t ubiblock_read(struct request *req){	struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req);	struct ubiblock *dev = req->q->queuedata;	u64 pos = blk_rq_pos(req) << 9;	int to_read = blk_rq_bytes(req);	int bytes_left = to_read;	/* Get LEB:offset address to read from */	int offset = do_div(pos, dev->leb_size);	int leb = pos;	struct req_iterator iter;	struct bio_vec bvec;	int ret; 	blk_mq_start_request(req); 	/*	 * It is safe to ignore the return value of blk_rq_map_sg() because	 * the number of sg entries is limited to UBI_MAX_SG_COUNT	 * and ubi_read_sg() will check that limit.	 */	ubi_sgl_init(&pdu->usgl);	blk_rq_map_sg(req, pdu->usgl.sg); 	while (bytes_left) {		/*		 * We can only read one LEB at a time. Therefore if the read		 * length is larger than one LEB size, we split the operation.		 */		if (offset + to_read > dev->leb_size)			to_read = dev->leb_size - offset; 		ret = ubi_read_sg(dev->desc, leb, &pdu->usgl, offset, to_read);		if (ret < 0)			break; 		bytes_left -= to_read;		to_read = bytes_left;		leb += 1;		offset = 0;	} 	rq_for_each_segment(bvec, req, iter)		flush_dcache_page(bvec.bv_page); 	blk_mq_end_request(req, errno_to_blk_status(ret)); 	return BLK_STS_OK;} static int ubiblock_open(struct gendisk *disk, blk_mode_t mode){	struct ubiblock *dev = disk->private_data;	int ret; 	mutex_lock(&dev->dev_mutex);	if (dev->refcnt > 0) {		/*		 * The volume is already open, just increase the reference		 * counter.		 */		goto out_done;	} 	/*	 * We want users to be aware they should only mount us as read-only.	 * It's just a paranoid check, as write requests will get rejected	 * in any case.	 */	if (mode & BLK_OPEN_WRITE) {		ret = -EROFS;		goto out_unlock;	}	dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY);	if (IS_ERR(dev->desc)) {		dev_err(disk_to_dev(dev->gd), "failed to open ubi volume %d_%d",			dev->ubi_num, dev->vol_id);		ret = PTR_ERR(dev->desc);		dev->desc = NULL;		goto out_unlock;	} out_done:	dev->refcnt++;	mutex_unlock(&dev->dev_mutex);	return 0; out_unlock:	mutex_unlock(&dev->dev_mutex);	return ret;} static void ubiblock_release(struct gendisk *gd){	struct ubiblock *dev = gd->private_data; 	mutex_lock(&dev->dev_mutex);	dev->refcnt--;	if (dev->refcnt == 0) {		ubi_close_volume(dev->desc);		dev->desc = NULL;	}	mutex_unlock(&dev->dev_mutex);} static int ubiblock_getgeo(struct gendisk *disk, struct hd_geometry *geo){	/* Some tools might require this information */	geo->heads = 1;	geo->cylinders = 1;	geo->sectors = get_capacity(disk);	geo->start = 0;	return 0;} static const struct block_device_operations ubiblock_ops = {	.owner = THIS_MODULE,	.open = ubiblock_open,	.release = ubiblock_release,	.getgeo	= ubiblock_getgeo,}; static blk_status_t ubiblock_queue_rq(struct blk_mq_hw_ctx *hctx,			     const struct blk_mq_queue_data *bd){	switch (req_op(bd->rq)) {	case REQ_OP_READ:		return ubiblock_read(bd->rq);	default:		return BLK_STS_IOERR;	}} static int ubiblock_init_request(struct blk_mq_tag_set *set,		struct request *req, unsigned int hctx_idx,		int numa_node){	struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); 	sg_init_table(pdu->usgl.sg, UBI_MAX_SG_COUNT);	return 0;} static const struct blk_mq_ops ubiblock_mq_ops = {	.queue_rq       = ubiblock_queue_rq,	.init_request	= ubiblock_init_request,}; static int calc_disk_capacity(struct ubi_volume_info *vi, u64 *disk_capacity){	u64 size = vi->used_bytes >> 9; 	if (vi->used_bytes % 512) {		if (vi->vol_type == UBI_DYNAMIC_VOLUME)			pr_warn("UBI: block: volume size is not a multiple of 512, last %llu bytes are ignored!\n",				vi->used_bytes - (size << 9));		else			pr_info("UBI: block: volume size is not a multiple of 512, last %llu bytes are ignored!\n",				vi->used_bytes - (size << 9));	} 	if ((sector_t)size != size)		return -EFBIG; 	*disk_capacity = size; 	return 0;} int ubiblock_create(struct ubi_volume_info *vi){	struct queue_limits lim = {		.max_segments		= UBI_MAX_SG_COUNT,	};	struct ubiblock *dev;	struct gendisk *gd;	u64 disk_capacity;	int ret; 	ret = calc_disk_capacity(vi, &disk_capacity);	if (ret) {		return ret;	} 	/* Check that the volume isn't already handled */	mutex_lock(&devices_mutex);	if (find_dev_nolock(vi->ubi_num, vi->vol_id)) {		ret = -EEXIST;		goto out_unlock;	} 	dev = kzalloc_obj(struct ubiblock);	if (!dev) {		ret = -ENOMEM;		goto out_unlock;	} 	mutex_init(&dev->dev_mutex); 	dev->ubi_num = vi->ubi_num;	dev->vol_id = vi->vol_id;	dev->leb_size = vi->usable_leb_size; 	dev->tag_set.ops = &ubiblock_mq_ops;	dev->tag_set.queue_depth = 64;	dev->tag_set.numa_node = NUMA_NO_NODE;	dev->tag_set.flags = BLK_MQ_F_BLOCKING;	dev->tag_set.cmd_size = sizeof(struct ubiblock_pdu);	dev->tag_set.driver_data = dev;	dev->tag_set.nr_hw_queues = 1; 	ret = blk_mq_alloc_tag_set(&dev->tag_set);	if (ret) {		pr_err("ubiblock%d_%d: blk_mq_alloc_tag_set failed\n",			dev->ubi_num, dev->vol_id);		goto out_free_dev;	}  	/* Initialize the gendisk of this ubiblock device */	gd = blk_mq_alloc_disk(&dev->tag_set, &lim, dev);	if (IS_ERR(gd)) {		ret = PTR_ERR(gd);		goto out_free_tags;	} 	gd->fops = &ubiblock_ops;	gd->major = ubiblock_major;	gd->minors = 1;	gd->first_minor = idr_alloc(&ubiblock_minor_idr, dev, 0, 0, GFP_KERNEL);	if (gd->first_minor < 0) {		pr_err("ubiblock%d_%d: block: dynamic minor allocation failed\n",			dev->ubi_num, dev->vol_id);		ret = -ENODEV;		goto out_cleanup_disk;	}	gd->flags |= GENHD_FL_NO_PART;	gd->private_data = dev;	sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);	set_capacity(gd, disk_capacity);	dev->gd = gd; 	dev->rq = gd->queue; 	list_add_tail(&dev->list, &ubiblock_devices); 	/* Must be the last step: anyone can call file ops from now on */	ret = device_add_disk(vi->dev, dev->gd, NULL);	if (ret)		goto out_remove_minor; 	dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)",		 dev->ubi_num, dev->vol_id, vi->name);	mutex_unlock(&devices_mutex);	return 0; out_remove_minor:	list_del(&dev->list);	idr_remove(&ubiblock_minor_idr, gd->first_minor);out_cleanup_disk:	put_disk(gd);out_free_tags:	blk_mq_free_tag_set(&dev->tag_set);out_free_dev:	kfree(dev);out_unlock:	mutex_unlock(&devices_mutex); 	return ret;} static void ubiblock_cleanup(struct ubiblock *dev){	int id = dev->gd->first_minor; 	/* Stop new requests to arrive */	del_gendisk(dev->gd);	/* Finally destroy the blk queue */	dev_info(disk_to_dev(dev->gd), "released");	put_disk(dev->gd);	blk_mq_free_tag_set(&dev->tag_set);	idr_remove(&ubiblock_minor_idr, id);} int ubiblock_remove(struct ubi_volume_info *vi){	struct ubiblock *dev;	int ret; 	mutex_lock(&devices_mutex);	dev = find_dev_nolock(vi->ubi_num, vi->vol_id);	if (!dev) {		ret = -ENODEV;		goto out_unlock;	} 	/* Found a device, let's lock it so we can check if it's busy */	mutex_lock(&dev->dev_mutex);	if (dev->refcnt > 0) {		ret = -EBUSY;		goto out_unlock_dev;	} 	/* Remove from device list */	list_del(&dev->list);	ubiblock_cleanup(dev);	mutex_unlock(&dev->dev_mutex);	mutex_unlock(&devices_mutex); 	kfree(dev);	return 0; out_unlock_dev:	mutex_unlock(&dev->dev_mutex);out_unlock:	mutex_unlock(&devices_mutex);	return ret;} static int ubiblock_resize(struct ubi_volume_info *vi){

Lines 1–500 of 678.