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: ...@@ -350,6 +350,8 @@ prototypes:
unsigned int (*poll) (struct file *, struct poll_table_struct *); unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long); 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 (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *); int (*open) (struct inode *, struct file *);
int (*flush) (struct file *); int (*flush) (struct file *);
...@@ -383,6 +385,8 @@ aio_write: no ...@@ -383,6 +385,8 @@ aio_write: no
readdir: no readdir: no
poll: no poll: no
ioctl: yes (see below) ioctl: yes (see below)
unlocked_ioctl: no (see below)
compat_ioctl: no
mmap: no mmap: no
open: maybe (see below) open: maybe (see below)
flush: no flush: no
...@@ -428,6 +432,9 @@ move ->readdir() to inode_operations and use a separate method for directory ...@@ -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 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... 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 ->read on directories probably must go away - we should just enforce -EISDIR
in sys_read() and friends. in sys_read() and friends.
......
...@@ -397,56 +397,17 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -397,56 +397,17 @@ int unregister_ioctl32_conversion(unsigned int cmd)
} }
EXPORT_SYMBOL(unregister_ioctl32_conversion); EXPORT_SYMBOL(unregister_ioctl32_conversion);
asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, static void compat_ioctl_error(struct file *filp, unsigned int fd,
unsigned long arg) 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 buf[10];
char *fn = "?"; char *fn = "?";
char *path; char *path;
path = (char *)__get_free_page(GFP_KERNEL);
/* find the name of the device. */ /* find the name of the device. */
path = (char *)__get_free_page(GFP_KERNEL);
if (path) { if (path) {
fn = d_path(filp->f_dentry, fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE);
filp->f_vfsmnt, path,
PAGE_SIZE);
if (IS_ERR(fn)) if (IS_ERR(fn))
fn = "?"; fn = "?";
} }
...@@ -459,15 +420,67 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, ...@@ -459,15 +420,67 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
current->comm, current->pid, current->comm, current->pid,
(int)fd, (unsigned int)cmd, buf, (int)fd, (unsigned int)cmd, buf,
(unsigned int)arg, fn); (unsigned int)arg, fn);
if (path) if (path)
free_page((unsigned long)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; 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); up_read(&ioctl32_sem);
out2: do_ioctl:
error = sys_ioctl(fd, cmd, arg);
out_fput:
fput_light(filp, fput_needed);
out:
return error; return error;
} }
......
...@@ -16,7 +16,32 @@ ...@@ -16,7 +16,32 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/ioctls.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 error;
int block; int block;
...@@ -36,7 +61,9 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) ...@@ -36,7 +61,9 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
if ((error = get_user(block, p)) != 0) if ((error = get_user(block, p)) != 0)
return error; return error;
lock_kernel();
res = mapping->a_ops->bmap(mapping, block); res = mapping->a_ops->bmap(mapping, block);
unlock_kernel();
return put_user(res, p); return put_user(res, p);
} }
case FIGETBSZ: case FIGETBSZ:
...@@ -46,9 +73,8 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) ...@@ -46,9 +73,8 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
case FIONREAD: case FIONREAD:
return put_user(i_size_read(inode) - filp->f_pos, p); 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 do_ioctl(filp, cmd, arg);
return -ENOTTY;
} }
...@@ -57,18 +83,16 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) ...@@ -57,18 +83,16 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
struct file * filp; struct file * filp;
unsigned int flag; unsigned int flag;
int on, error = -EBADF; int on, error = -EBADF;
int fput_needed;
filp = fget(fd); filp = fget_light(fd, &fput_needed);
if (!filp) if (!filp)
goto out; goto out;
error = security_file_ioctl(filp, cmd, arg); error = security_file_ioctl(filp, cmd, arg);
if (error) { if (error)
fput(filp); goto out_fput;
goto out;
}
lock_kernel();
switch (cmd) { switch (cmd) {
case FIOCLEX: case FIOCLEX:
set_close_on_exec(fd, 1); set_close_on_exec(fd, 1);
...@@ -100,8 +124,11 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) ...@@ -100,8 +124,11 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
/* Did FASYNC state change ? */ /* Did FASYNC state change ? */
if ((flag ^ filp->f_flags) & FASYNC) { 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); error = filp->f_op->fasync(fd, filp, on);
unlock_kernel();
}
else error = -ENOTTY; else error = -ENOTTY;
} }
if (error != 0) if (error != 0)
...@@ -124,16 +151,15 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) ...@@ -124,16 +151,15 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
error = -ENOTTY; error = -ENOTTY;
break; break;
default: default:
error = -ENOTTY;
if (S_ISREG(filp->f_dentry->d_inode->i_mode)) if (S_ISREG(filp->f_dentry->d_inode->i_mode))
error = file_ioctl(filp, cmd, arg); error = file_ioctl(filp, cmd, arg);
else if (filp->f_op && filp->f_op->ioctl) else
error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); error = do_ioctl(filp, cmd, arg);
break;
} }
unlock_kernel(); out_fput:
fput(filp); fput_light(filp, fput_needed);
out:
out:
return error; return error;
} }
......
...@@ -907,8 +907,8 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u ...@@ -907,8 +907,8 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u
/* /*
* NOTE: * NOTE:
* read, write, poll, fsync, readv, writev can be called * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
* without the big kernel lock held in all filesystems. * can be called without the big kernel lock held in all filesystems.
*/ */
struct file_operations { struct file_operations {
struct module *owner; struct module *owner;
...@@ -920,6 +920,8 @@ struct file_operations { ...@@ -920,6 +920,8 @@ struct file_operations {
int (*readdir) (struct file *, void *, filldir_t); int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *); unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 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 (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *); int (*open) (struct inode *, struct file *);
int (*flush) (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