162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * bsg.c - block layer implementation of the sg v4 interface 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/file.h> 862306a36Sopenharmony_ci#include <linux/blkdev.h> 962306a36Sopenharmony_ci#include <linux/cdev.h> 1062306a36Sopenharmony_ci#include <linux/jiffies.h> 1162306a36Sopenharmony_ci#include <linux/percpu.h> 1262306a36Sopenharmony_ci#include <linux/idr.h> 1362306a36Sopenharmony_ci#include <linux/bsg.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <scsi/scsi.h> 1762306a36Sopenharmony_ci#include <scsi/scsi_ioctl.h> 1862306a36Sopenharmony_ci#include <scsi/sg.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define BSG_DESCRIPTION "Block layer SCSI generic (bsg) driver" 2162306a36Sopenharmony_ci#define BSG_VERSION "0.4" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct bsg_device { 2462306a36Sopenharmony_ci struct request_queue *queue; 2562306a36Sopenharmony_ci struct device device; 2662306a36Sopenharmony_ci struct cdev cdev; 2762306a36Sopenharmony_ci int max_queue; 2862306a36Sopenharmony_ci unsigned int timeout; 2962306a36Sopenharmony_ci unsigned int reserved_size; 3062306a36Sopenharmony_ci bsg_sg_io_fn *sg_io_fn; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline struct bsg_device *to_bsg_device(struct inode *inode) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return container_of(inode->i_cdev, struct bsg_device, cdev); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define BSG_DEFAULT_CMDS 64 3962306a36Sopenharmony_ci#define BSG_MAX_DEVS (1 << MINORBITS) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic DEFINE_IDA(bsg_minor_ida); 4262306a36Sopenharmony_cistatic const struct class bsg_class; 4362306a36Sopenharmony_cistatic int bsg_major; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic unsigned int bsg_timeout(struct bsg_device *bd, struct sg_io_v4 *hdr) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci unsigned int timeout = BLK_DEFAULT_SG_TIMEOUT; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (hdr->timeout) 5062306a36Sopenharmony_ci timeout = msecs_to_jiffies(hdr->timeout); 5162306a36Sopenharmony_ci else if (bd->timeout) 5262306a36Sopenharmony_ci timeout = bd->timeout; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return max_t(unsigned int, timeout, BLK_MIN_SG_TIMEOUT); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int bsg_sg_io(struct bsg_device *bd, bool open_for_write, 5862306a36Sopenharmony_ci void __user *uarg) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct sg_io_v4 hdr; 6162306a36Sopenharmony_ci int ret; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (copy_from_user(&hdr, uarg, sizeof(hdr))) 6462306a36Sopenharmony_ci return -EFAULT; 6562306a36Sopenharmony_ci if (hdr.guard != 'Q') 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci ret = bd->sg_io_fn(bd->queue, &hdr, open_for_write, 6862306a36Sopenharmony_ci bsg_timeout(bd, &hdr)); 6962306a36Sopenharmony_ci if (!ret && copy_to_user(uarg, &hdr, sizeof(hdr))) 7062306a36Sopenharmony_ci return -EFAULT; 7162306a36Sopenharmony_ci return ret; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int bsg_open(struct inode *inode, struct file *file) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci if (!blk_get_queue(to_bsg_device(inode)->queue)) 7762306a36Sopenharmony_ci return -ENXIO; 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int bsg_release(struct inode *inode, struct file *file) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci blk_put_queue(to_bsg_device(inode)->queue); 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int bsg_get_command_q(struct bsg_device *bd, int __user *uarg) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci return put_user(READ_ONCE(bd->max_queue), uarg); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int bsg_set_command_q(struct bsg_device *bd, int __user *uarg) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int max_queue; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (get_user(max_queue, uarg)) 9762306a36Sopenharmony_ci return -EFAULT; 9862306a36Sopenharmony_ci if (max_queue < 1) 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci WRITE_ONCE(bd->max_queue, max_queue); 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct bsg_device *bd = to_bsg_device(file_inode(file)); 10762306a36Sopenharmony_ci struct request_queue *q = bd->queue; 10862306a36Sopenharmony_ci void __user *uarg = (void __user *) arg; 10962306a36Sopenharmony_ci int __user *intp = uarg; 11062306a36Sopenharmony_ci int val; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci switch (cmd) { 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * Our own ioctls 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci case SG_GET_COMMAND_Q: 11762306a36Sopenharmony_ci return bsg_get_command_q(bd, uarg); 11862306a36Sopenharmony_ci case SG_SET_COMMAND_Q: 11962306a36Sopenharmony_ci return bsg_set_command_q(bd, uarg); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * SCSI/sg ioctls 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci case SG_GET_VERSION_NUM: 12562306a36Sopenharmony_ci return put_user(30527, intp); 12662306a36Sopenharmony_ci case SCSI_IOCTL_GET_IDLUN: 12762306a36Sopenharmony_ci return put_user(0, intp); 12862306a36Sopenharmony_ci case SCSI_IOCTL_GET_BUS_NUMBER: 12962306a36Sopenharmony_ci return put_user(0, intp); 13062306a36Sopenharmony_ci case SG_SET_TIMEOUT: 13162306a36Sopenharmony_ci if (get_user(val, intp)) 13262306a36Sopenharmony_ci return -EFAULT; 13362306a36Sopenharmony_ci bd->timeout = clock_t_to_jiffies(val); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci case SG_GET_TIMEOUT: 13662306a36Sopenharmony_ci return jiffies_to_clock_t(bd->timeout); 13762306a36Sopenharmony_ci case SG_GET_RESERVED_SIZE: 13862306a36Sopenharmony_ci return put_user(min(bd->reserved_size, queue_max_bytes(q)), 13962306a36Sopenharmony_ci intp); 14062306a36Sopenharmony_ci case SG_SET_RESERVED_SIZE: 14162306a36Sopenharmony_ci if (get_user(val, intp)) 14262306a36Sopenharmony_ci return -EFAULT; 14362306a36Sopenharmony_ci if (val < 0) 14462306a36Sopenharmony_ci return -EINVAL; 14562306a36Sopenharmony_ci bd->reserved_size = 14662306a36Sopenharmony_ci min_t(unsigned int, val, queue_max_bytes(q)); 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci case SG_EMULATED_HOST: 14962306a36Sopenharmony_ci return put_user(1, intp); 15062306a36Sopenharmony_ci case SG_IO: 15162306a36Sopenharmony_ci return bsg_sg_io(bd, file->f_mode & FMODE_WRITE, uarg); 15262306a36Sopenharmony_ci case SCSI_IOCTL_SEND_COMMAND: 15362306a36Sopenharmony_ci pr_warn_ratelimited("%s: calling unsupported SCSI_IOCTL_SEND_COMMAND\n", 15462306a36Sopenharmony_ci current->comm); 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci default: 15762306a36Sopenharmony_ci return -ENOTTY; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic const struct file_operations bsg_fops = { 16262306a36Sopenharmony_ci .open = bsg_open, 16362306a36Sopenharmony_ci .release = bsg_release, 16462306a36Sopenharmony_ci .unlocked_ioctl = bsg_ioctl, 16562306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 16662306a36Sopenharmony_ci .owner = THIS_MODULE, 16762306a36Sopenharmony_ci .llseek = default_llseek, 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void bsg_device_release(struct device *dev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct bsg_device *bd = container_of(dev, struct bsg_device, device); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ida_free(&bsg_minor_ida, MINOR(bd->device.devt)); 17562306a36Sopenharmony_ci kfree(bd); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_civoid bsg_unregister_queue(struct bsg_device *bd) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct gendisk *disk = bd->queue->disk; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (disk && disk->queue_kobj.sd) 18362306a36Sopenharmony_ci sysfs_remove_link(&disk->queue_kobj, "bsg"); 18462306a36Sopenharmony_ci cdev_device_del(&bd->cdev, &bd->device); 18562306a36Sopenharmony_ci put_device(&bd->device); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bsg_unregister_queue); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistruct bsg_device *bsg_register_queue(struct request_queue *q, 19062306a36Sopenharmony_ci struct device *parent, const char *name, bsg_sg_io_fn *sg_io_fn) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct bsg_device *bd; 19362306a36Sopenharmony_ci int ret; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci bd = kzalloc(sizeof(*bd), GFP_KERNEL); 19662306a36Sopenharmony_ci if (!bd) 19762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 19862306a36Sopenharmony_ci bd->max_queue = BSG_DEFAULT_CMDS; 19962306a36Sopenharmony_ci bd->reserved_size = INT_MAX; 20062306a36Sopenharmony_ci bd->queue = q; 20162306a36Sopenharmony_ci bd->sg_io_fn = sg_io_fn; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = ida_alloc_max(&bsg_minor_ida, BSG_MAX_DEVS - 1, GFP_KERNEL); 20462306a36Sopenharmony_ci if (ret < 0) { 20562306a36Sopenharmony_ci if (ret == -ENOSPC) 20662306a36Sopenharmony_ci dev_err(parent, "bsg: too many bsg devices\n"); 20762306a36Sopenharmony_ci kfree(bd); 20862306a36Sopenharmony_ci return ERR_PTR(ret); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci bd->device.devt = MKDEV(bsg_major, ret); 21162306a36Sopenharmony_ci bd->device.class = &bsg_class; 21262306a36Sopenharmony_ci bd->device.parent = parent; 21362306a36Sopenharmony_ci bd->device.release = bsg_device_release; 21462306a36Sopenharmony_ci dev_set_name(&bd->device, "%s", name); 21562306a36Sopenharmony_ci device_initialize(&bd->device); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci cdev_init(&bd->cdev, &bsg_fops); 21862306a36Sopenharmony_ci bd->cdev.owner = THIS_MODULE; 21962306a36Sopenharmony_ci ret = cdev_device_add(&bd->cdev, &bd->device); 22062306a36Sopenharmony_ci if (ret) 22162306a36Sopenharmony_ci goto out_put_device; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (q->disk && q->disk->queue_kobj.sd) { 22462306a36Sopenharmony_ci ret = sysfs_create_link(&q->disk->queue_kobj, &bd->device.kobj, 22562306a36Sopenharmony_ci "bsg"); 22662306a36Sopenharmony_ci if (ret) 22762306a36Sopenharmony_ci goto out_device_del; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return bd; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciout_device_del: 23362306a36Sopenharmony_ci cdev_device_del(&bd->cdev, &bd->device); 23462306a36Sopenharmony_ciout_put_device: 23562306a36Sopenharmony_ci put_device(&bd->device); 23662306a36Sopenharmony_ci return ERR_PTR(ret); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bsg_register_queue); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic char *bsg_devnode(const struct device *dev, umode_t *mode) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "bsg/%s", dev_name(dev)); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic const struct class bsg_class = { 24662306a36Sopenharmony_ci .name = "bsg", 24762306a36Sopenharmony_ci .devnode = bsg_devnode, 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int __init bsg_init(void) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci dev_t devid; 25362306a36Sopenharmony_ci int ret; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = class_register(&bsg_class); 25662306a36Sopenharmony_ci if (ret) 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ret = alloc_chrdev_region(&devid, 0, BSG_MAX_DEVS, "bsg"); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci goto destroy_bsg_class; 26262306a36Sopenharmony_ci bsg_major = MAJOR(devid); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci printk(KERN_INFO BSG_DESCRIPTION " version " BSG_VERSION 26562306a36Sopenharmony_ci " loaded (major %d)\n", bsg_major); 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cidestroy_bsg_class: 26962306a36Sopenharmony_ci class_unregister(&bsg_class); 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ciMODULE_AUTHOR("Jens Axboe"); 27462306a36Sopenharmony_ciMODULE_DESCRIPTION(BSG_DESCRIPTION); 27562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cidevice_initcall(bsg_init); 278