Blame view

drivers/char/raw.c 6.94 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * linux/drivers/char/raw.c
   *
   * Front-end raw character devices.  These can be bound to any block
   * devices to provide genuine Unix raw character device semantics.
   *
   * We reserve minor number 0 for a control interface.  ioctl()s on this
   * device are used to bind the other minor numbers to block devices.
   */
  
  #include <linux/init.h>
  #include <linux/fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
  #include <linux/major.h>
  #include <linux/blkdev.h>
  #include <linux/module.h>
  #include <linux/raw.h>
  #include <linux/capability.h>
  #include <linux/uio.h>
  #include <linux/cdev.h>
  #include <linux/device.h>
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
21
  #include <linux/mutex.h>
c0bed680f   Jonathan Corbet   raw: BKL pushdown
22
  #include <linux/smp_lock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
  
  #include <asm/uaccess.h>
  
  struct raw_device_data {
  	struct block_device *binding;
  	int inuse;
  };
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
30
  static struct class *raw_class;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  static struct raw_device_data raw_devices[MAX_RAW_MINORS];
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
32
  static DEFINE_MUTEX(raw_mutex);
62322d255   Arjan van de Ven   [PATCH] make more...
33
  static const struct file_operations raw_ctl_fops; /* forward declaration */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  
  /*
   * Open/close code for raw IO.
   *
   * We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to
   * point at the blockdev's address_space and set the file handle to use
   * O_DIRECT.
   *
   * Set the device's soft blocksize to the minimum possible.  This gives the
   * finest possible alignment and has no adverse impact on performance.
   */
  static int raw_open(struct inode *inode, struct file *filp)
  {
  	const int minor = iminor(inode);
  	struct block_device *bdev;
  	int err;
  
  	if (minor == 0) {	/* It is the control device */
  		filp->f_op = &raw_ctl_fops;
  		return 0;
  	}
c0bed680f   Jonathan Corbet   raw: BKL pushdown
55
  	lock_kernel();
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
56
  	mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
62
63
64
65
  
  	/*
  	 * All we need to do on open is check that the device is bound.
  	 */
  	bdev = raw_devices[minor].binding;
  	err = -ENODEV;
  	if (!bdev)
  		goto out;
  	igrab(bdev->bd_inode);
572c48921   Al Viro   [PATCH] sanitize ...
66
  	err = blkdev_get(bdev, filp->f_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
72
73
74
75
76
77
  	if (err)
  		goto out;
  	err = bd_claim(bdev, raw_open);
  	if (err)
  		goto out1;
  	err = set_blocksize(bdev, bdev_hardsect_size(bdev));
  	if (err)
  		goto out2;
  	filp->f_flags |= O_DIRECT;
  	filp->f_mapping = bdev->bd_inode->i_mapping;
  	if (++raw_devices[minor].inuse == 1)
a7113a966   Josef Sipek   [PATCH] struct pa...
78
  		filp->f_path.dentry->d_inode->i_mapping =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
  			bdev->bd_inode->i_mapping;
  	filp->private_data = bdev;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
81
  	mutex_unlock(&raw_mutex);
c0bed680f   Jonathan Corbet   raw: BKL pushdown
82
  	unlock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
  	return 0;
  
  out2:
  	bd_release(bdev);
  out1:
9a1c35427   Al Viro   [PATCH] pass fmod...
88
  	blkdev_put(bdev, filp->f_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  out:
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
90
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
97
98
99
100
101
  	return err;
  }
  
  /*
   * When the final fd which refers to this character-special node is closed, we
   * make its ->mapping point back at its own i_data.
   */
  static int raw_release(struct inode *inode, struct file *filp)
  {
  	const int minor= iminor(inode);
  	struct block_device *bdev;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
102
  	mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
107
108
  	bdev = raw_devices[minor].binding;
  	if (--raw_devices[minor].inuse == 0) {
  		/* Here  inode->i_mapping == bdev->bd_inode->i_mapping  */
  		inode->i_mapping = &inode->i_data;
  		inode->i_mapping->backing_dev_info = &default_backing_dev_info;
  	}
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
109
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
  
  	bd_release(bdev);
9a1c35427   Al Viro   [PATCH] pass fmod...
112
  	blkdev_put(bdev, filp->f_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
121
122
123
  	return 0;
  }
  
  /*
   * Forward ioctls to the underlying block device.
   */
  static int
  raw_ioctl(struct inode *inode, struct file *filp,
  		  unsigned int command, unsigned long arg)
  {
  	struct block_device *bdev = filp->private_data;
56b26add0   Al Viro   [PATCH] kill the ...
124
  	return blkdev_ioctl(bdev, 0, command, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
  }
  
  static void bind_device(struct raw_config_request *rq)
  {
38ca6c34d   Greg Kroah-Hartman   Driver core: conv...
129
  	device_destroy(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor));
03457cd45   Greg Kroah-Hartman   device create: ch...
130
131
  	device_create(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor), NULL,
  		      "raw%d", rq->raw_minor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  }
  
  /*
   * Deal with ioctls against the raw-device control interface, to bind
   * and unbind other raw devices.
   */
  static int raw_ctl_ioctl(struct inode *inode, struct file *filp,
  			unsigned int command, unsigned long arg)
  {
  	struct raw_config_request rq;
  	struct raw_device_data *rawdev;
  	int err = 0;
  
  	switch (command) {
  	case RAW_SETBIND:
  	case RAW_GETBIND:
  
  		/* First, find out which raw minor we want */
  
  		if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) {
  			err = -EFAULT;
  			goto out;
  		}
38584c14b   Jeff Moyer   [PATCH] raw: don'...
155
  		if (rq.raw_minor <= 0 || rq.raw_minor >= MAX_RAW_MINORS) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  			err = -EINVAL;
  			goto out;
  		}
  		rawdev = &raw_devices[rq.raw_minor];
  
  		if (command == RAW_SETBIND) {
  			dev_t dev;
  
  			/*
  			 * This is like making block devices, so demand the
  			 * same capability
  			 */
  			if (!capable(CAP_SYS_ADMIN)) {
  				err = -EPERM;
  				goto out;
  			}
  
  			/*
  			 * For now, we don't need to check that the underlying
  			 * block device is present or not: we can do that when
  			 * the raw device is opened.  Just check that the
  			 * major/minor numbers make sense.
  			 */
  
  			dev = MKDEV(rq.block_major, rq.block_minor);
  			if ((rq.block_major == 0 && rq.block_minor != 0) ||
  					MAJOR(dev) != rq.block_major ||
  					MINOR(dev) != rq.block_minor) {
  				err = -EINVAL;
  				goto out;
  			}
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
187
  			mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  			if (rawdev->inuse) {
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
189
  				mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
198
199
  				err = -EBUSY;
  				goto out;
  			}
  			if (rawdev->binding) {
  				bdput(rawdev->binding);
  				module_put(THIS_MODULE);
  			}
  			if (rq.block_major == 0 && rq.block_minor == 0) {
  				/* unbind */
  				rawdev->binding = NULL;
38ca6c34d   Greg Kroah-Hartman   Driver core: conv...
200
  				device_destroy(raw_class,
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
201
  						MKDEV(RAW_MAJOR, rq.raw_minor));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
209
210
  			} else {
  				rawdev->binding = bdget(dev);
  				if (rawdev->binding == NULL)
  					err = -ENOMEM;
  				else {
  					__module_get(THIS_MODULE);
  					bind_device(&rq);
  				}
  			}
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
211
  			mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
  		} else {
  			struct block_device *bdev;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
214
  			mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
220
221
  			bdev = rawdev->binding;
  			if (bdev) {
  				rq.block_major = MAJOR(bdev->bd_dev);
  				rq.block_minor = MINOR(bdev->bd_dev);
  			} else {
  				rq.block_major = rq.block_minor = 0;
  			}
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
222
  			mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
229
230
231
232
233
234
235
  			if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) {
  				err = -EFAULT;
  				goto out;
  			}
  		}
  		break;
  	default:
  		err = -EINVAL;
  		break;
  	}
  out:
  	return err;
  }
62322d255   Arjan van de Ven   [PATCH] make more...
236
  static const struct file_operations raw_fops = {
543ade1fc   Badari Pulavarty   [PATCH] Streamlin...
237
  	.read	=	do_sync_read,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  	.aio_read = 	generic_file_aio_read,
543ade1fc   Badari Pulavarty   [PATCH] Streamlin...
239
  	.write	=	do_sync_write,
027445c37   Badari Pulavarty   [PATCH] Vectorize...
240
  	.aio_write = 	generic_file_aio_write_nolock,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
  	.open	=	raw_open,
  	.release=	raw_release,
  	.ioctl	=	raw_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
  	.owner	=	THIS_MODULE,
  };
62322d255   Arjan van de Ven   [PATCH] make more...
246
  static const struct file_operations raw_ctl_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
  	.ioctl	=	raw_ctl_ioctl,
  	.open	=	raw_open,
  	.owner	=	THIS_MODULE,
  };
7e7654a92   Greg Kroah-Hartman   cdev: remove unne...
251
  static struct cdev raw_cdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
  
  static int __init raw_init(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  	dev_t dev = MKDEV(RAW_MAJOR, 0);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
256
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257

3e26a423e   Rolf Eike Beer   [PATCH] Return be...
258
259
  	ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw");
  	if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
  		goto error;
  
  	cdev_init(&raw_cdev, &raw_fops);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
263
264
  	ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS);
  	if (ret) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  		kobject_put(&raw_cdev.kobj);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
266
  		goto error_region;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  	}
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
268
  	raw_class = class_create(THIS_MODULE, "raw");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
  	if (IS_ERR(raw_class)) {
  		printk(KERN_ERR "Error creating raw class.
  ");
  		cdev_del(&raw_cdev);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
273
274
  		ret = PTR_ERR(raw_class);
  		goto error_region;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	}
03457cd45   Greg Kroah-Hartman   device create: ch...
276
  	device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  	return 0;
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
279
280
  error_region:
  	unregister_chrdev_region(dev, MAX_RAW_MINORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  error:
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
282
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
  }
  
  static void __exit raw_exit(void)
  {
38ca6c34d   Greg Kroah-Hartman   Driver core: conv...
287
  	device_destroy(raw_class, MKDEV(RAW_MAJOR, 0));
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
288
  	class_destroy(raw_class);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
294
295
  	cdev_del(&raw_cdev);
  	unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS);
  }
  
  module_init(raw_init);
  module_exit(raw_exit);
  MODULE_LICENSE("GPL");