Commit 8b635618 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

[PATCH] ioctl rework #2

- add ->unlocked_ioctl method and a do_ioctl wrapper in ioctl.c so all
  places calling ->ioctl get it.  THis provides us a patch to migrate away
  from holding bkl across ioctl implementations.

- add ->compat_ioctl method and call it in compat_sys_ioctl before doing
  the hash lookup for registered handlers.

- streamline compat_sys_ioctl and move the complex error reporting into a
  function of its own

From: "Michael S. Tsirkin" <mst@mellanox.co.il>

Handle generic ioctl commands by falling back on static conversion
functions in fs/compat_ioctl.c on -ENOIOCTLCMD code.

From: "Michael S. Tsirkin" <mst@mellanox.co.il>

With new unlocked_ioctl and ioctl_compat, ioctls can now be as fast as
read/write.  So lets use fget_light/fput_light there, to get some speedup
in common case on SMP.
Signed-off-by: default avatarMichael s. Tsirkin <mst@mellanox.co.il>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 887ff81a
......@@ -350,6 +350,8 @@ prototypes:
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
......@@ -383,6 +385,8 @@ aio_write: no
readdir: no
poll: no
ioctl: yes (see below)
unlocked_ioctl: no (see below)
compat_ioctl: no
mmap: no
open: maybe (see below)
flush: no
......@@ -428,6 +432,9 @@ move ->readdir() to inode_operations and use a separate method for directory
anything that resembles union-mount we won't have a struct file for all
components. And there are other reasons why the current interface is a mess...
->ioctl() on regular files is superceded by the ->unlocked_ioctl() that
doesn't take the BKL.
->read on directories probably must go away - we should just enforce -EISDIR
in sys_read() and friends.
......
......@@ -397,56 +397,17 @@ int unregister_ioctl32_conversion(unsigned int cmd)
}
EXPORT_SYMBOL(unregister_ioctl32_conversion);
asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg)
static void compat_ioctl_error(struct file *filp, unsigned int fd,
unsigned int cmd, unsigned long arg)
{
struct file * filp;
int error = -EBADF;
struct ioctl_trans *t;
filp = fget(fd);
if(!filp)
goto out2;
if (!filp->f_op || !filp->f_op->ioctl) {
error = sys_ioctl (fd, cmd, arg);
goto out;
}
down_read(&ioctl32_sem);
t = ioctl32_hash_table[ioctl32_hash (cmd)];
while (t && t->cmd != cmd)
t = t->next;
if (t) {
if (t->handler) {
lock_kernel();
error = t->handler(fd, cmd, arg, filp);
unlock_kernel();
up_read(&ioctl32_sem);
} else {
up_read(&ioctl32_sem);
error = sys_ioctl(fd, cmd, arg);
}
} else {
up_read(&ioctl32_sem);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
error = siocdevprivate_ioctl(fd, cmd, arg);
} else {
static int count;
if (++count <= 50) {
char buf[10];
char *fn = "?";
char *path;
path = (char *)__get_free_page(GFP_KERNEL);
/* find the name of the device. */
path = (char *)__get_free_page(GFP_KERNEL);
if (path) {
fn = d_path(filp->f_dentry,
filp->f_vfsmnt, path,
PAGE_SIZE);
fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE);
if (IS_ERR(fn))
fn = "?";
}
......@@ -459,15 +420,67 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
current->comm, current->pid,
(int)fd, (unsigned int)cmd, buf,
(unsigned int)arg, fn);
if (path)
free_page((unsigned long)path);
}
asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg)
{
struct file *filp;
int error = -EBADF;
struct ioctl_trans *t;
int fput_needed;
filp = fget_light(fd, &fput_needed);
if (!filp)
goto out;
if (filp->f_op && filp->f_op->compat_ioctl) {
error = filp->f_op->compat_ioctl(filp, cmd, arg);
if (error != -ENOIOCTLCMD)
goto out_fput;
}
if (!filp->f_op ||
(!filp->f_op->ioctl && !filp->f_op->unlocked_ioctl))
goto do_ioctl;
down_read(&ioctl32_sem);
for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) {
if (t->cmd == cmd)
goto found_handler;
}
up_read(&ioctl32_sem);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
error = siocdevprivate_ioctl(fd, cmd, arg);
} else {
static int count;
if (++count <= 50)
compat_ioctl_error(filp, fd, cmd, arg);
error = -EINVAL;
}
goto out_fput;
found_handler:
if (t->handler) {
lock_kernel();
error = t->handler(fd, cmd, arg, filp);
unlock_kernel();
up_read(&ioctl32_sem);
goto out_fput;
}
out:
fput(filp);
out2:
up_read(&ioctl32_sem);
do_ioctl:
error = sys_ioctl(fd, cmd, arg);
out_fput:
fput_light(filp, fput_needed);
out:
return error;
}
......
......@@ -16,7 +16,32 @@
#include <asm/uaccess.h>
#include <asm/ioctls.h>
static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
static long do_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int error = -ENOTTY;
if (!filp->f_op)
goto out;
if (filp->f_op->unlocked_ioctl) {
error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
if (error == -ENOIOCTLCMD)
error = -EINVAL;
goto out;
} else if (filp->f_op->ioctl) {
lock_kernel();
error = filp->f_op->ioctl(filp->f_dentry->d_inode,
filp, cmd, arg);
unlock_kernel();
}
out:
return error;
}
static int file_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int error;
int block;
......@@ -36,7 +61,9 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
if ((error = get_user(block, p)) != 0)
return error;
lock_kernel();
res = mapping->a_ops->bmap(mapping, block);
unlock_kernel();
return put_user(res, p);
}
case FIGETBSZ:
......@@ -46,9 +73,8 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
case FIONREAD:
return put_user(i_size_read(inode) - filp->f_pos, p);
}
if (filp->f_op && filp->f_op->ioctl)
return filp->f_op->ioctl(inode, filp, cmd, arg);
return -ENOTTY;
return do_ioctl(filp, cmd, arg);
}
......@@ -57,18 +83,16 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
struct file * filp;
unsigned int flag;
int on, error = -EBADF;
int fput_needed;
filp = fget(fd);
filp = fget_light(fd, &fput_needed);
if (!filp)
goto out;
error = security_file_ioctl(filp, cmd, arg);
if (error) {
fput(filp);
goto out;
}
if (error)
goto out_fput;
lock_kernel();
switch (cmd) {
case FIOCLEX:
set_close_on_exec(fd, 1);
......@@ -100,8 +124,11 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
/* Did FASYNC state change ? */
if ((flag ^ filp->f_flags) & FASYNC) {
if (filp->f_op && filp->f_op->fasync)
if (filp->f_op && filp->f_op->fasync) {
lock_kernel();
error = filp->f_op->fasync(fd, filp, on);
unlock_kernel();
}
else error = -ENOTTY;
}
if (error != 0)
......@@ -124,16 +151,15 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
error = -ENOTTY;
break;
default:
error = -ENOTTY;
if (S_ISREG(filp->f_dentry->d_inode->i_mode))
error = file_ioctl(filp, cmd, arg);
else if (filp->f_op && filp->f_op->ioctl)
error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
else
error = do_ioctl(filp, cmd, arg);
break;
}
unlock_kernel();
fput(filp);
out:
out_fput:
fput_light(filp, fput_needed);
out:
return error;
}
......
......@@ -907,8 +907,8 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u
/*
* NOTE:
* read, write, poll, fsync, readv, writev can be called
* without the big kernel lock held in all filesystems.
* read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
* can be called without the big kernel lock held in all filesystems.
*/
struct file_operations {
struct module *owner;
......@@ -920,6 +920,8 @@ struct file_operations {
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment