Commit 6043dddc authored by Roland Dreier's avatar Roland Dreier Committed by Linus Torvalds

[PATCH] fs/compat.c: rwsem instead of BKL around ioctl32_hash_table

Currently the BKL is used to synchronize access to ioctl32_hash_table in
fs/compat.c.  It seems that an rwsem would be more appropriate, since this
would allow multiple lookups to occur in parallel (and also serve the
general good of minimizing use of the BKL).

I added lock_kernel()/unlock_kernel() around the call to t->handler when a
compatibility handler is found in compat_sys_ioctl() to preserve the
expectation that the BKL will be held during driver ioctl operations.  It
should be safe to do lock_kernel() while holding ioctl32_sem because of the
magic BKL sleep semantics.
Signed-off-by: default avatarRoland Dreier <roland@topspin.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fa77470d
......@@ -41,6 +41,7 @@
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/syscall.h>
#include <linux/personality.h>
#include <linux/rwsem.h>
#include <net/sock.h> /* siocdevprivate_ioctl */
......@@ -247,7 +248,8 @@ asmlinkage long compat_fstatfs64(unsigned int fd, compat_size_t sz, struct compa
/* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */
#define IOCTL_HASHSIZE 256
struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
static DECLARE_RWSEM(ioctl32_sem);
extern struct ioctl_trans ioctl_start[];
extern int ioctl_table_size;
......@@ -302,12 +304,12 @@ int register_ioctl32_conversion(unsigned int cmd,
if (!new_t)
return -ENOMEM;
lock_kernel();
down_write(&ioctl32_sem);
for (t = ioctl32_hash_table[hash]; t; t = t->next) {
if (t->cmd == cmd) {
printk(KERN_ERR "Trying to register duplicated ioctl32 "
"handler %x\n", cmd);
unlock_kernel();
up_write(&ioctl32_sem);
kfree(new_t);
return -EINVAL;
}
......@@ -317,7 +319,7 @@ int register_ioctl32_conversion(unsigned int cmd,
new_t->handler = handler;
ioctl32_insert_translation(new_t);
unlock_kernel();
up_write(&ioctl32_sem);
return 0;
}
EXPORT_SYMBOL(register_ioctl32_conversion);
......@@ -337,11 +339,11 @@ int unregister_ioctl32_conversion(unsigned int cmd)
unsigned long hash = ioctl32_hash(cmd);
struct ioctl_trans *t, *t1;
lock_kernel();
down_write(&ioctl32_sem);
t = ioctl32_hash_table[hash];
if (!t) {
unlock_kernel();
up_write(&ioctl32_sem);
return -EINVAL;
}
......@@ -351,7 +353,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
__builtin_return_address(0), cmd);
} else {
ioctl32_hash_table[hash] = t->next;
unlock_kernel();
up_write(&ioctl32_sem);
kfree(t);
return 0;
}
......@@ -366,7 +368,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
goto out;
} else {
t->next = t1->next;
unlock_kernel();
up_write(&ioctl32_sem);
kfree(t1);
return 0;
}
......@@ -376,7 +378,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n",
cmd);
out:
unlock_kernel();
up_write(&ioctl32_sem);
return -EINVAL;
}
EXPORT_SYMBOL(unregister_ioctl32_conversion);
......@@ -397,7 +399,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
goto out;
}
lock_kernel();
down_read(&ioctl32_sem);
t = ioctl32_hash_table[ioctl32_hash (cmd)];
......@@ -405,14 +407,16 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int 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 {
unlock_kernel();
up_read(&ioctl32_sem);
error = sys_ioctl(fd, cmd, arg);
}
} else {
unlock_kernel();
up_read(&ioctl32_sem);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
error = siocdevprivate_ioctl(fd, cmd, arg);
} else {
......
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