Blame view
block/bsg.c
6.12 KB
8c16567d8
|
1 |
// SPDX-License-Identifier: GPL-2.0 |
3d6392cfb
|
2 |
/* |
0c6a89ba6
|
3 |
* bsg.c - block layer implementation of the sg v4 interface |
3d6392cfb
|
4 |
*/ |
3d6392cfb
|
5 6 7 8 |
#include <linux/module.h> #include <linux/init.h> #include <linux/file.h> #include <linux/blkdev.h> |
3d6392cfb
|
9 |
#include <linux/cdev.h> |
ad5ebd2fa
|
10 |
#include <linux/jiffies.h> |
3d6392cfb
|
11 |
#include <linux/percpu.h> |
598443a21
|
12 |
#include <linux/idr.h> |
3d6392cfb
|
13 |
#include <linux/bsg.h> |
5a0e3ad6a
|
14 |
#include <linux/slab.h> |
3d6392cfb
|
15 16 17 |
#include <scsi/scsi.h> #include <scsi/scsi_ioctl.h> |
3d6392cfb
|
18 |
#include <scsi/sg.h> |
0ed081ce2
|
19 20 |
#define BSG_DESCRIPTION "Block layer SCSI generic (bsg) driver" #define BSG_VERSION "0.4" |
3d6392cfb
|
21 |
|
3d6392cfb
|
22 |
struct bsg_device { |
165125e1e
|
23 |
struct request_queue *queue; |
ead09dd3a
|
24 25 |
struct device device; struct cdev cdev; |
3d6392cfb
|
26 |
int max_queue; |
1e61c1a80
|
27 28 |
unsigned int timeout; unsigned int reserved_size; |
75ca56409
|
29 |
bsg_sg_io_fn *sg_io_fn; |
3d6392cfb
|
30 |
}; |
ead09dd3a
|
31 32 33 34 |
static inline struct bsg_device *to_bsg_device(struct inode *inode) { return container_of(inode->i_cdev, struct bsg_device, cdev); } |
5309cb38d
|
35 |
#define BSG_DEFAULT_CMDS 64 |
292b7f271
|
36 |
#define BSG_MAX_DEVS 32768 |
3d6392cfb
|
37 |
|
ead09dd3a
|
38 |
static DEFINE_IDA(bsg_minor_ida); |
3d6392cfb
|
39 |
static struct class *bsg_class; |
46f6ef4af
|
40 |
static int bsg_major; |
3d6392cfb
|
41 |
|
75ca56409
|
42 43 44 45 46 47 48 49 50 51 52 |
static unsigned int bsg_timeout(struct bsg_device *bd, struct sg_io_v4 *hdr) { unsigned int timeout = BLK_DEFAULT_SG_TIMEOUT; if (hdr->timeout) timeout = msecs_to_jiffies(hdr->timeout); else if (bd->timeout) timeout = bd->timeout; return max_t(unsigned int, timeout, BLK_MIN_SG_TIMEOUT); } |
17cb960f2
|
53 |
|
ead09dd3a
|
54 |
static int bsg_sg_io(struct bsg_device *bd, fmode_t mode, void __user *uarg) |
3d6392cfb
|
55 |
{ |
ccf3209f0
|
56 |
struct sg_io_v4 hdr; |
aebf526b5
|
57 |
int ret; |
c7a841f3a
|
58 |
|
ccf3209f0
|
59 60 |
if (copy_from_user(&hdr, uarg, sizeof(hdr))) return -EFAULT; |
ccf3209f0
|
61 62 |
if (hdr.guard != 'Q') return -EINVAL; |
75ca56409
|
63 |
ret = bd->sg_io_fn(bd->queue, &hdr, mode, bsg_timeout(bd, &hdr)); |
972248e91
|
64 |
if (!ret && copy_to_user(uarg, &hdr, sizeof(hdr))) |
ccf3209f0
|
65 66 |
return -EFAULT; return ret; |
70e36ecea
|
67 |
} |
3d6392cfb
|
68 69 |
static int bsg_open(struct inode *inode, struct file *file) { |
ead09dd3a
|
70 71 |
if (!blk_get_queue(to_bsg_device(inode)->queue)) return -ENXIO; |
3d6392cfb
|
72 73 74 75 76 |
return 0; } static int bsg_release(struct inode *inode, struct file *file) { |
ead09dd3a
|
77 78 |
blk_put_queue(to_bsg_device(inode)->queue); return 0; |
3d6392cfb
|
79 |
} |
ccf3209f0
|
80 81 |
static int bsg_get_command_q(struct bsg_device *bd, int __user *uarg) { |
ead09dd3a
|
82 |
return put_user(READ_ONCE(bd->max_queue), uarg); |
ccf3209f0
|
83 84 85 86 |
} static int bsg_set_command_q(struct bsg_device *bd, int __user *uarg) { |
ead09dd3a
|
87 |
int max_queue; |
ccf3209f0
|
88 |
|
ead09dd3a
|
89 |
if (get_user(max_queue, uarg)) |
ccf3209f0
|
90 |
return -EFAULT; |
ead09dd3a
|
91 |
if (max_queue < 1) |
ccf3209f0
|
92 |
return -EINVAL; |
ead09dd3a
|
93 |
WRITE_ONCE(bd->max_queue, max_queue); |
ccf3209f0
|
94 95 |
return 0; } |
25fd16430
|
96 |
static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
3d6392cfb
|
97 |
{ |
ead09dd3a
|
98 |
struct bsg_device *bd = to_bsg_device(file_inode(file)); |
d52fe8f43
|
99 |
struct request_queue *q = bd->queue; |
ccf3209f0
|
100 |
void __user *uarg = (void __user *) arg; |
d52fe8f43
|
101 102 |
int __user *intp = uarg; int val; |
3d6392cfb
|
103 |
|
3d6392cfb
|
104 |
switch (cmd) { |
ccf3209f0
|
105 106 107 |
/* * Our own ioctls */ |
3d6392cfb
|
108 |
case SG_GET_COMMAND_Q: |
ccf3209f0
|
109 110 111 |
return bsg_get_command_q(bd, uarg); case SG_SET_COMMAND_Q: return bsg_set_command_q(bd, uarg); |
3d6392cfb
|
112 113 114 115 116 |
/* * SCSI/sg ioctls */ case SG_GET_VERSION_NUM: |
d52fe8f43
|
117 |
return put_user(30527, intp); |
3d6392cfb
|
118 |
case SCSI_IOCTL_GET_IDLUN: |
d52fe8f43
|
119 |
return put_user(0, intp); |
3d6392cfb
|
120 |
case SCSI_IOCTL_GET_BUS_NUMBER: |
d52fe8f43
|
121 |
return put_user(0, intp); |
3d6392cfb
|
122 |
case SG_SET_TIMEOUT: |
d52fe8f43
|
123 124 |
if (get_user(val, intp)) return -EFAULT; |
1e61c1a80
|
125 |
bd->timeout = clock_t_to_jiffies(val); |
d52fe8f43
|
126 |
return 0; |
3d6392cfb
|
127 |
case SG_GET_TIMEOUT: |
1e61c1a80
|
128 |
return jiffies_to_clock_t(bd->timeout); |
3d6392cfb
|
129 |
case SG_GET_RESERVED_SIZE: |
1e61c1a80
|
130 |
return put_user(min(bd->reserved_size, queue_max_bytes(q)), |
d52fe8f43
|
131 |
intp); |
3d6392cfb
|
132 |
case SG_SET_RESERVED_SIZE: |
d52fe8f43
|
133 134 135 136 |
if (get_user(val, intp)) return -EFAULT; if (val < 0) return -EINVAL; |
1e61c1a80
|
137 |
bd->reserved_size = |
d52fe8f43
|
138 139 |
min_t(unsigned int, val, queue_max_bytes(q)); return 0; |
3d6392cfb
|
140 |
case SG_EMULATED_HOST: |
d52fe8f43
|
141 |
return put_user(1, intp); |
ccf3209f0
|
142 |
case SG_IO: |
ead09dd3a
|
143 |
return bsg_sg_io(bd, file->f_mode, uarg); |
beec64d0c
|
144 145 146 147 148 |
case SCSI_IOCTL_SEND_COMMAND: pr_warn_ratelimited("%s: calling unsupported SCSI_IOCTL_SEND_COMMAND ", current->comm); return -EINVAL; |
3d6392cfb
|
149 |
default: |
3d6392cfb
|
150 |
return -ENOTTY; |
3d6392cfb
|
151 152 |
} } |
7344be053
|
153 |
static const struct file_operations bsg_fops = { |
3d6392cfb
|
154 155 |
.open = bsg_open, .release = bsg_release, |
25fd16430
|
156 |
.unlocked_ioctl = bsg_ioctl, |
fe0da4e5e
|
157 |
.compat_ioctl = compat_ptr_ioctl, |
3d6392cfb
|
158 |
.owner = THIS_MODULE, |
6038f373a
|
159 |
.llseek = default_llseek, |
3d6392cfb
|
160 |
}; |
1a0db7744
|
161 162 163 164 165 166 167 |
static void bsg_device_release(struct device *dev) { struct bsg_device *bd = container_of(dev, struct bsg_device, device); ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt)); kfree(bd); } |
ead09dd3a
|
168 |
void bsg_unregister_queue(struct bsg_device *bd) |
3d6392cfb
|
169 |
{ |
ead09dd3a
|
170 171 172 |
if (bd->queue->kobj.sd) sysfs_remove_link(&bd->queue->kobj, "bsg"); cdev_device_del(&bd->cdev, &bd->device); |
1a0db7744
|
173 |
put_device(&bd->device); |
3d6392cfb
|
174 |
} |
4cf0723ac
|
175 |
EXPORT_SYMBOL_GPL(bsg_unregister_queue); |
3d6392cfb
|
176 |
|
ead09dd3a
|
177 |
struct bsg_device *bsg_register_queue(struct request_queue *q, |
75ca56409
|
178 |
struct device *parent, const char *name, bsg_sg_io_fn *sg_io_fn) |
3d6392cfb
|
179 |
{ |
ead09dd3a
|
180 |
struct bsg_device *bd; |
bab998d62
|
181 |
int ret; |
3d6392cfb
|
182 |
|
ead09dd3a
|
183 184 185 186 |
bd = kzalloc(sizeof(*bd), GFP_KERNEL); if (!bd) return ERR_PTR(-ENOMEM); bd->max_queue = BSG_DEFAULT_CMDS; |
1e61c1a80
|
187 |
bd->reserved_size = INT_MAX; |
ead09dd3a
|
188 |
bd->queue = q; |
75ca56409
|
189 |
bd->sg_io_fn = sg_io_fn; |
292b7f271
|
190 |
|
ead09dd3a
|
191 |
ret = ida_simple_get(&bsg_minor_ida, 0, BSG_MAX_DEVS, GFP_KERNEL); |
bab998d62
|
192 |
if (ret < 0) { |
ead09dd3a
|
193 194 195 |
if (ret == -ENOSPC) dev_err(parent, "bsg: too many bsg devices "); |
1a0db7744
|
196 197 |
kfree(bd); return ERR_PTR(ret); |
4e2872d6b
|
198 |
} |
ead09dd3a
|
199 200 201 |
bd->device.devt = MKDEV(bsg_major, ret); bd->device.class = bsg_class; bd->device.parent = parent; |
1a0db7744
|
202 |
bd->device.release = bsg_device_release; |
ead09dd3a
|
203 204 205 206 207 208 209 |
dev_set_name(&bd->device, "%s", name); device_initialize(&bd->device); cdev_init(&bd->cdev, &bsg_fops); bd->cdev.owner = THIS_MODULE; ret = cdev_device_add(&bd->cdev, &bd->device); if (ret) |
1a0db7744
|
210 |
goto out_put_device; |
4e2872d6b
|
211 |
|
abce891a1
|
212 |
if (q->kobj.sd) { |
ead09dd3a
|
213 |
ret = sysfs_create_link(&q->kobj, &bd->device.kobj, "bsg"); |
4e2872d6b
|
214 |
if (ret) |
ead09dd3a
|
215 |
goto out_device_del; |
4e2872d6b
|
216 |
} |
ead09dd3a
|
217 |
return bd; |
6826ee4fd
|
218 |
|
ead09dd3a
|
219 220 |
out_device_del: cdev_device_del(&bd->cdev, &bd->device); |
1a0db7744
|
221 222 |
out_put_device: put_device(&bd->device); |
ead09dd3a
|
223 |
return ERR_PTR(ret); |
4e2872d6b
|
224 |
} |
780110426
|
225 |
EXPORT_SYMBOL_GPL(bsg_register_queue); |
4e2872d6b
|
226 |
|
2c9ede55e
|
227 |
static char *bsg_devnode(struct device *dev, umode_t *mode) |
2bdf91491
|
228 229 230 |
{ return kasprintf(GFP_KERNEL, "bsg/%s", dev_name(dev)); } |
3d6392cfb
|
231 232 |
static int __init bsg_init(void) { |
46f6ef4af
|
233 |
dev_t devid; |
ead09dd3a
|
234 |
int ret; |
3d6392cfb
|
235 236 |
bsg_class = class_create(THIS_MODULE, "bsg"); |
28519c891
|
237 238 |
if (IS_ERR(bsg_class)) return PTR_ERR(bsg_class); |
e454cea20
|
239 |
bsg_class->devnode = bsg_devnode; |
3d6392cfb
|
240 |
|
46f6ef4af
|
241 |
ret = alloc_chrdev_region(&devid, 0, BSG_MAX_DEVS, "bsg"); |
9b9f770ce
|
242 243 |
if (ret) goto destroy_bsg_class; |
46f6ef4af
|
244 |
bsg_major = MAJOR(devid); |
5d3a8cd34
|
245 |
printk(KERN_INFO BSG_DESCRIPTION " version " BSG_VERSION |
0ed081ce2
|
246 247 |
" loaded (major %d) ", bsg_major); |
3d6392cfb
|
248 |
return 0; |
ead09dd3a
|
249 |
|
9b9f770ce
|
250 251 |
destroy_bsg_class: class_destroy(bsg_class); |
9b9f770ce
|
252 |
return ret; |
3d6392cfb
|
253 254 255 |
} MODULE_AUTHOR("Jens Axboe"); |
0ed081ce2
|
256 |
MODULE_DESCRIPTION(BSG_DESCRIPTION); |
3d6392cfb
|
257 |
MODULE_LICENSE("GPL"); |
4e2872d6b
|
258 |
device_initcall(bsg_init); |