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 @@ ...@@ -41,6 +41,7 @@
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#include <linux/nfsd/syscall.h> #include <linux/nfsd/syscall.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/rwsem.h>
#include <net/sock.h> /* siocdevprivate_ioctl */ #include <net/sock.h> /* siocdevprivate_ioctl */
...@@ -247,7 +248,8 @@ asmlinkage long compat_fstatfs64(unsigned int fd, compat_size_t sz, struct compa ...@@ -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 */ /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */
#define IOCTL_HASHSIZE 256 #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 struct ioctl_trans ioctl_start[];
extern int ioctl_table_size; extern int ioctl_table_size;
...@@ -302,12 +304,12 @@ int register_ioctl32_conversion(unsigned int cmd, ...@@ -302,12 +304,12 @@ int register_ioctl32_conversion(unsigned int cmd,
if (!new_t) if (!new_t)
return -ENOMEM; return -ENOMEM;
lock_kernel(); down_write(&ioctl32_sem);
for (t = ioctl32_hash_table[hash]; t; t = t->next) { for (t = ioctl32_hash_table[hash]; t; t = t->next) {
if (t->cmd == cmd) { if (t->cmd == cmd) {
printk(KERN_ERR "Trying to register duplicated ioctl32 " printk(KERN_ERR "Trying to register duplicated ioctl32 "
"handler %x\n", cmd); "handler %x\n", cmd);
unlock_kernel(); up_write(&ioctl32_sem);
kfree(new_t); kfree(new_t);
return -EINVAL; return -EINVAL;
} }
...@@ -317,7 +319,7 @@ int register_ioctl32_conversion(unsigned int cmd, ...@@ -317,7 +319,7 @@ int register_ioctl32_conversion(unsigned int cmd,
new_t->handler = handler; new_t->handler = handler;
ioctl32_insert_translation(new_t); ioctl32_insert_translation(new_t);
unlock_kernel(); up_write(&ioctl32_sem);
return 0; return 0;
} }
EXPORT_SYMBOL(register_ioctl32_conversion); EXPORT_SYMBOL(register_ioctl32_conversion);
...@@ -337,11 +339,11 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -337,11 +339,11 @@ int unregister_ioctl32_conversion(unsigned int cmd)
unsigned long hash = ioctl32_hash(cmd); unsigned long hash = ioctl32_hash(cmd);
struct ioctl_trans *t, *t1; struct ioctl_trans *t, *t1;
lock_kernel(); down_write(&ioctl32_sem);
t = ioctl32_hash_table[hash]; t = ioctl32_hash_table[hash];
if (!t) { if (!t) {
unlock_kernel(); up_write(&ioctl32_sem);
return -EINVAL; return -EINVAL;
} }
...@@ -351,7 +353,7 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -351,7 +353,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
__builtin_return_address(0), cmd); __builtin_return_address(0), cmd);
} else { } else {
ioctl32_hash_table[hash] = t->next; ioctl32_hash_table[hash] = t->next;
unlock_kernel(); up_write(&ioctl32_sem);
kfree(t); kfree(t);
return 0; return 0;
} }
...@@ -366,7 +368,7 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -366,7 +368,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
goto out; goto out;
} else { } else {
t->next = t1->next; t->next = t1->next;
unlock_kernel(); up_write(&ioctl32_sem);
kfree(t1); kfree(t1);
return 0; return 0;
} }
...@@ -376,7 +378,7 @@ int unregister_ioctl32_conversion(unsigned int cmd) ...@@ -376,7 +378,7 @@ int unregister_ioctl32_conversion(unsigned int cmd)
printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n",
cmd); cmd);
out: out:
unlock_kernel(); up_write(&ioctl32_sem);
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL(unregister_ioctl32_conversion); EXPORT_SYMBOL(unregister_ioctl32_conversion);
...@@ -397,7 +399,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, ...@@ -397,7 +399,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
goto out; goto out;
} }
lock_kernel(); down_read(&ioctl32_sem);
t = ioctl32_hash_table[ioctl32_hash (cmd)]; t = ioctl32_hash_table[ioctl32_hash (cmd)];
...@@ -405,14 +407,16 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, ...@@ -405,14 +407,16 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
t = t->next; t = t->next;
if (t) { if (t) {
if (t->handler) { if (t->handler) {
lock_kernel();
error = t->handler(fd, cmd, arg, filp); error = t->handler(fd, cmd, arg, filp);
unlock_kernel(); unlock_kernel();
up_read(&ioctl32_sem);
} else { } else {
unlock_kernel(); up_read(&ioctl32_sem);
error = sys_ioctl(fd, cmd, arg); error = sys_ioctl(fd, cmd, arg);
} }
} else { } else {
unlock_kernel(); up_read(&ioctl32_sem);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
error = siocdevprivate_ioctl(fd, cmd, arg); error = siocdevprivate_ioctl(fd, cmd, arg);
} else { } 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