Commit bb93e3a52f8db7210258a1a2134cced0b78a46e1
Committed by
Linus Torvalds
1 parent
0d77e5a2c2
Exists in
master
and in
4 other branches
[PATCH] block: add unlocked_ioctl support for block devices
This patch allows block device drivers to convert their ioctl functions to unlocked_ioctl() like character devices and other subsystems. All functions that were called with the BKL held before are still used that way, but I would not be surprised if it could be removed from the ioctl functions in drivers/block/ioctl.c themselves. As a side note, I found that compat_blkdev_ioctl() acquires the BKL as well, which looks like a bug. I have checked that every user of disk->fops->compat_ioctl() in the current git tree gets the BKL itself, so it could easily be removed from compat_blkdev_ioctl(). Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 3 changed files with 57 additions and 23 deletions Side-by-side Diff
drivers/block/ioctl.c
... | ... | @@ -133,11 +133,9 @@ |
133 | 133 | return put_user(val, (u64 __user *)arg); |
134 | 134 | } |
135 | 135 | |
136 | -int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, | |
137 | - unsigned long arg) | |
136 | +static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev, | |
137 | + unsigned cmd, unsigned long arg) | |
138 | 138 | { |
139 | - struct block_device *bdev = inode->i_bdev; | |
140 | - struct gendisk *disk = bdev->bd_disk; | |
141 | 139 | struct backing_dev_info *bdi; |
142 | 140 | int ret, n; |
143 | 141 | |
144 | 142 | |
145 | 143 | |
146 | 144 | |
147 | 145 | |
148 | 146 | |
149 | 147 | |
150 | 148 | |
151 | 149 | |
... | ... | @@ -190,36 +188,72 @@ |
190 | 188 | return put_ulong(arg, bdev->bd_inode->i_size >> 9); |
191 | 189 | case BLKGETSIZE64: |
192 | 190 | return put_u64(arg, bdev->bd_inode->i_size); |
191 | + } | |
192 | + return -ENOIOCTLCMD; | |
193 | +} | |
194 | + | |
195 | +static int blkdev_driver_ioctl(struct inode *inode, struct file *file, | |
196 | + struct gendisk *disk, unsigned cmd, unsigned long arg) | |
197 | +{ | |
198 | + int ret; | |
199 | + if (disk->fops->unlocked_ioctl) | |
200 | + return disk->fops->unlocked_ioctl(file, cmd, arg); | |
201 | + | |
202 | + if (disk->fops->ioctl) { | |
203 | + lock_kernel(); | |
204 | + ret = disk->fops->ioctl(inode, file, cmd, arg); | |
205 | + unlock_kernel(); | |
206 | + return ret; | |
207 | + } | |
208 | + | |
209 | + return -ENOTTY; | |
210 | +} | |
211 | + | |
212 | +int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, | |
213 | + unsigned long arg) | |
214 | +{ | |
215 | + struct block_device *bdev = inode->i_bdev; | |
216 | + struct gendisk *disk = bdev->bd_disk; | |
217 | + int ret, n; | |
218 | + | |
219 | + switch(cmd) { | |
193 | 220 | case BLKFLSBUF: |
194 | 221 | if (!capable(CAP_SYS_ADMIN)) |
195 | 222 | return -EACCES; |
196 | - if (disk->fops->ioctl) { | |
197 | - ret = disk->fops->ioctl(inode, file, cmd, arg); | |
198 | - /* -EINVAL to handle old uncorrected drivers */ | |
199 | - if (ret != -EINVAL && ret != -ENOTTY) | |
200 | - return ret; | |
201 | - } | |
223 | + | |
224 | + ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); | |
225 | + /* -EINVAL to handle old uncorrected drivers */ | |
226 | + if (ret != -EINVAL && ret != -ENOTTY) | |
227 | + return ret; | |
228 | + | |
229 | + lock_kernel(); | |
202 | 230 | fsync_bdev(bdev); |
203 | 231 | invalidate_bdev(bdev, 0); |
232 | + unlock_kernel(); | |
204 | 233 | return 0; |
234 | + | |
205 | 235 | case BLKROSET: |
206 | - if (disk->fops->ioctl) { | |
207 | - ret = disk->fops->ioctl(inode, file, cmd, arg); | |
208 | - /* -EINVAL to handle old uncorrected drivers */ | |
209 | - if (ret != -EINVAL && ret != -ENOTTY) | |
210 | - return ret; | |
211 | - } | |
236 | + ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); | |
237 | + /* -EINVAL to handle old uncorrected drivers */ | |
238 | + if (ret != -EINVAL && ret != -ENOTTY) | |
239 | + return ret; | |
212 | 240 | if (!capable(CAP_SYS_ADMIN)) |
213 | 241 | return -EACCES; |
214 | 242 | if (get_user(n, (int __user *)(arg))) |
215 | 243 | return -EFAULT; |
244 | + lock_kernel(); | |
216 | 245 | set_device_ro(bdev, n); |
246 | + unlock_kernel(); | |
217 | 247 | return 0; |
218 | - default: | |
219 | - if (disk->fops->ioctl) | |
220 | - return disk->fops->ioctl(inode, file, cmd, arg); | |
221 | 248 | } |
222 | - return -ENOTTY; | |
249 | + | |
250 | + lock_kernel(); | |
251 | + ret = blkdev_locked_ioctl(file, bdev, cmd, arg); | |
252 | + unlock_kernel(); | |
253 | + if (ret != -ENOIOCTLCMD) | |
254 | + return ret; | |
255 | + | |
256 | + return blkdev_driver_ioctl(inode, file, disk, cmd, arg); | |
223 | 257 | } |
224 | 258 | |
225 | 259 | /* Most of the generic ioctls are handled in the normal fallback path. |
fs/block_dev.c
... | ... | @@ -777,8 +777,7 @@ |
777 | 777 | return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); |
778 | 778 | } |
779 | 779 | |
780 | -static int block_ioctl(struct inode *inode, struct file *file, unsigned cmd, | |
781 | - unsigned long arg) | |
780 | +static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) | |
782 | 781 | { |
783 | 782 | return blkdev_ioctl(file->f_mapping->host, file, cmd, arg); |
784 | 783 | } |
... | ... | @@ -803,7 +802,7 @@ |
803 | 802 | .aio_write = blkdev_file_aio_write, |
804 | 803 | .mmap = generic_file_mmap, |
805 | 804 | .fsync = block_fsync, |
806 | - .ioctl = block_ioctl, | |
805 | + .unlocked_ioctl = block_ioctl, | |
807 | 806 | #ifdef CONFIG_COMPAT |
808 | 807 | .compat_ioctl = compat_blkdev_ioctl, |
809 | 808 | #endif |
include/linux/fs.h
... | ... | @@ -884,6 +884,7 @@ |
884 | 884 | int (*open) (struct inode *, struct file *); |
885 | 885 | int (*release) (struct inode *, struct file *); |
886 | 886 | int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); |
887 | + long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); | |
887 | 888 | long (*compat_ioctl) (struct file *, unsigned, unsigned long); |
888 | 889 | int (*media_changed) (struct gendisk *); |
889 | 890 | int (*revalidate_disk) (struct gendisk *); |