2003-02-20 22:23:01

by Pavel Machek

[permalink] [raw]
Subject: ioctl32 consolidation

Hi!

Currently, 32-bit emulation in kernel has *5* copies, and its >1000
lines each. Plus, locking of all but x86-64 architectures is broken
(I'm told by andi ;-).

So, here's patch that starts sharing sys32_ioctl() [as a first step],
which should rmove locking problems.

I've done the work for x86-64 and sparc64; if it looks good I'll
attempt to do other architectures. [Unless maintainers prefer to do it
themselves: I don't have easy access to 64-bit machines besides
hammer.]

Pavel

--- clean/arch/sparc64/kernel/ioctl32.c 2003-02-17 23:56:55.000000000 +0100
+++ linux-2.5.62/arch/sparc64/kernel/ioctl32.c 2003-02-20 22:18:29.000000000 +0100
@@ -677,7 +677,7 @@
return (void *) (usp - len);
}

-static int siocdevprivate_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+int siocdevprivate_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct ifreq *u_ifreq64;
struct ifreq32 *u_ifreq32 = (struct ifreq32 *) arg;
@@ -4264,16 +4264,10 @@
return sys_ioctl(fd, BLKGETSIZE64, arg);
}

-struct ioctl_trans {
- unsigned int cmd;
- unsigned int handler;
- unsigned int next;
-};
-
#define COMPATIBLE_IOCTL(cmd) asm volatile(".word %0, sys_ioctl, 0" : : "i" (cmd));
#define HANDLE_IOCTL(cmd,handler) asm volatile(".word %0, %1, 0" : : "i" (cmd), "i" (handler));
-#define IOCTL_TABLE_START void ioctl32_foo(void) { asm volatile(".data\nioctl_translations:");
-#define IOCTL_TABLE_END asm volatile("\nioctl_translations_end:\n\t.previous"); }
+#define IOCTL_TABLE_START void ioctl32_foo(void) { asm volatile(".data\n.global ioctl_start\nioctl_start:");
+#define IOCTL_TABLE_END asm volatile("\n.global ioctl_end\nioctl_end:\n\t.previous"); }

IOCTL_TABLE_START
/* List here exlicitly which ioctl's are known to have
@@ -5197,134 +5191,3 @@
HANDLE_IOCTL(BLKBSZSET_32, do_blkbszset)
HANDLE_IOCTL(BLKGETSIZE64_32, do_blkgetsize64)
IOCTL_TABLE_END
-
-unsigned int ioctl32_hash_table[1024];
-
-static inline unsigned long ioctl32_hash(unsigned long cmd)
-{
- return ((cmd >> 6) ^ (cmd >> 4) ^ cmd) & 0x3ff;
-}
-
-static void ioctl32_insert_translation(struct ioctl_trans *trans)
-{
- unsigned long hash;
- struct ioctl_trans *t;
-
- hash = ioctl32_hash (trans->cmd);
- if (!ioctl32_hash_table[hash])
- ioctl32_hash_table[hash] = (u32)(long)trans;
- else {
- t = (struct ioctl_trans *)(long)ioctl32_hash_table[hash];
- while (t->next)
- t = (struct ioctl_trans *)(long)t->next;
- trans->next = 0;
- t->next = (u32)(long)trans;
- }
-}
-
-static int __init init_sys32_ioctl(void)
-{
- int i;
- extern struct ioctl_trans ioctl_translations[], ioctl_translations_end[];
-
- for (i = 0; &ioctl_translations[i] < &ioctl_translations_end[0]; i++)
- ioctl32_insert_translation(&ioctl_translations[i]);
- return 0;
-}
-
-__initcall(init_sys32_ioctl);
-
-static struct ioctl_trans *additional_ioctls;
-
-/* Always call these with kernel lock held! */
-
-int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
-{
- int i;
- if (!additional_ioctls) {
- additional_ioctls = vmalloc(PAGE_SIZE);
- if (!additional_ioctls)
- return -ENOMEM;
- memset(additional_ioctls, 0, PAGE_SIZE);
- }
- for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++)
- if (!additional_ioctls[i].cmd)
- break;
- if (i == PAGE_SIZE/sizeof(struct ioctl_trans))
- return -ENOMEM;
- additional_ioctls[i].cmd = cmd;
- if (!handler)
- additional_ioctls[i].handler = (u32)(long)sys_ioctl;
- else
- additional_ioctls[i].handler = (u32)(long)handler;
- ioctl32_insert_translation(&additional_ioctls[i]);
- return 0;
-}
-
-int unregister_ioctl32_conversion(unsigned int cmd)
-{
- unsigned long hash = ioctl32_hash(cmd);
- struct ioctl_trans *t, *t1;
-
- t = (struct ioctl_trans *)(long)ioctl32_hash_table[hash];
- if (!t) return -EINVAL;
- if (t->cmd == cmd && t >= additional_ioctls &&
- (unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) {
- ioctl32_hash_table[hash] = t->next;
- t->cmd = 0;
- t->next = 0;
- return 0;
- } else while (t->next) {
- t1 = (struct ioctl_trans *)(long)t->next;
- if (t1->cmd == cmd && t1 >= additional_ioctls &&
- (unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) {
- t->next = t1->next;
- t1->cmd = 0;
- t1->next = 0;
- return 0;
- }
- t = t1;
- }
- return -EINVAL;
-}
-
-asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- struct file * filp;
- int error = -EBADF;
- int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
- 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;
- }
-
- t = (struct ioctl_trans *)(long)ioctl32_hash_table [ioctl32_hash (cmd)];
-
- while (t && t->cmd != cmd)
- t = (struct ioctl_trans *)(long)t->next;
- if (t) {
- handler = (void *)(long)t->handler;
- error = handler(fd, cmd, arg, filp);
- } else if (cmd >= SIOCDEVPRIVATE &&
- cmd <= (SIOCDEVPRIVATE + 15)) {
- error = siocdevprivate_ioctl(fd, cmd, arg);
- } else {
- static int count;
- if (++count <= 20)
- printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) "
- "cmd(%08x) arg(%08x)\n",
- current->comm, current->pid,
- (int)fd, (unsigned int)cmd, (unsigned int)arg);
- error = -EINVAL;
- }
-out:
- fput(filp);
-out2:
- return error;
-}
--- clean/arch/sparc64/kernel/sparc64_ksyms.c 2003-02-17 23:56:49.000000000 +0100
+++ linux-2.5.62/arch/sparc64/kernel/sparc64_ksyms.c 2003-02-20 22:26:39.000000000 +0100
@@ -89,7 +89,7 @@
extern int svr4_getcontext(svr4_ucontext_t *uc, struct pt_regs *regs);
extern int svr4_setcontext(svr4_ucontext_t *uc, struct pt_regs *regs);
extern int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
-extern int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+extern int compat_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
extern long sparc32_open(const char * filename, int flags, int mode);
extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
@@ -316,7 +316,7 @@
EXPORT_SYMBOL(svr4_setcontext);
EXPORT_SYMBOL(prom_cpu_nodes);
EXPORT_SYMBOL(sys_ioctl);
-EXPORT_SYMBOL(sys32_ioctl);
+EXPORT_SYMBOL(compat_ioctl);
EXPORT_SYMBOL(sparc32_open);
#endif

diff -ur clean/arch/sparc64/kernel/sunos_ioctl32.c linux-2.5.62/arch/sparc64/kernel/sunos_ioctl32.c
--- clean/arch/sparc64/kernel/sunos_ioctl32.c 2003-02-17 23:56:59.000000000 +0100
+++ linux-2.5.62/arch/sparc64/kernel/sunos_ioctl32.c 2003-02-20 22:22:54.000000000 +0100
@@ -92,7 +92,7 @@

extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);

-extern asmlinkage int sys32_ioctl(unsigned int, unsigned int, u32);
+extern asmlinkage int compat_ioctl(unsigned int, unsigned int, u32);
extern asmlinkage int sys_setsid(void);

asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg)
@@ -127,39 +127,39 @@
}
switch(cmd) {
case _IOW('r', 10, struct rtentry32):
- ret = sys32_ioctl(fd, SIOCADDRT, arg);
+ ret = compat_ioctl(fd, SIOCADDRT, arg);
goto out;
case _IOW('r', 11, struct rtentry32):
- ret = sys32_ioctl(fd, SIOCDELRT, arg);
+ ret = compat_ioctl(fd, SIOCDELRT, arg);
goto out;

case _IOW('i', 12, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFADDR, arg);
+ ret = compat_ioctl(fd, SIOCSIFADDR, arg);
goto out;
case _IOWR('i', 13, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFADDR, arg);
+ ret = compat_ioctl(fd, SIOCGIFADDR, arg);
goto out;
case _IOW('i', 14, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFDSTADDR, arg);
+ ret = compat_ioctl(fd, SIOCSIFDSTADDR, arg);
goto out;
case _IOWR('i', 15, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFDSTADDR, arg);
+ ret = compat_ioctl(fd, SIOCGIFDSTADDR, arg);
goto out;
case _IOW('i', 16, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFFLAGS, arg);
+ ret = compat_ioctl(fd, SIOCSIFFLAGS, arg);
goto out;
case _IOWR('i', 17, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFFLAGS, arg);
+ ret = compat_ioctl(fd, SIOCGIFFLAGS, arg);
goto out;
case _IOW('i', 18, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFMEM, arg);
+ ret = compat_ioctl(fd, SIOCSIFMEM, arg);
goto out;
case _IOWR('i', 19, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFMEM, arg);
+ ret = compat_ioctl(fd, SIOCGIFMEM, arg);
goto out;

case _IOWR('i', 20, struct ifconf32):
- ret = sys32_ioctl(fd, SIOCGIFCONF, arg);
+ ret = compat_ioctl(fd, SIOCGIFCONF, arg);
goto out;

case _IOW('i', 21, struct ifreq): /* SIOCSIFMTU */
@@ -170,32 +170,32 @@
goto out;

case _IOWR('i', 23, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
+ ret = compat_ioctl(fd, SIOCGIFBRDADDR, arg);
goto out;
case _IOW('i', 24, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFBRDADDR, arg);
+ ret = compat_ioctl(fd, SIOCSIFBRDADDR, arg);
goto out;
case _IOWR('i', 25, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg);
+ ret = compat_ioctl(fd, SIOCGIFNETMASK, arg);
goto out;
case _IOW('i', 26, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFNETMASK, arg);
+ ret = compat_ioctl(fd, SIOCSIFNETMASK, arg);
goto out;
case _IOWR('i', 27, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFMETRIC, arg);
+ ret = compat_ioctl(fd, SIOCGIFMETRIC, arg);
goto out;
case _IOW('i', 28, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCSIFMETRIC, arg);
+ ret = compat_ioctl(fd, SIOCSIFMETRIC, arg);
goto out;

case _IOW('i', 30, struct arpreq):
- ret = sys32_ioctl(fd, SIOCSARP, arg);
+ ret = compat_ioctl(fd, SIOCSARP, arg);
goto out;
case _IOWR('i', 31, struct arpreq):
- ret = sys32_ioctl(fd, SIOCGARP, arg);
+ ret = compat_ioctl(fd, SIOCGARP, arg);
goto out;
case _IOW('i', 32, struct arpreq):
- ret = sys32_ioctl(fd, SIOCDARP, arg);
+ ret = compat_ioctl(fd, SIOCDARP, arg);
goto out;

case _IOW('i', 40, struct ifreq32): /* SIOCUPPER */
@@ -209,10 +209,10 @@
goto out;

case _IOW('i', 49, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCADDMULTI, arg);
+ ret = compat_ioctl(fd, SIOCADDMULTI, arg);
goto out;
case _IOW('i', 50, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCDELMULTI, arg);
+ ret = compat_ioctl(fd, SIOCDELMULTI, arg);
goto out;

/* FDDI interface ioctls, unsupported. */
@@ -246,7 +246,7 @@
ret = -EFAULT;
if(get_user(oldval, ptr))
goto out;
- ret = sys32_ioctl(fd, cmd, arg);
+ ret = compat_ioctl(fd, cmd, arg);
__get_user(newval, ptr);
if(newval == -1) {
__put_user(oldval, ptr);
@@ -265,7 +265,7 @@
ret = -EFAULT;
if(get_user(oldval, ptr))
goto out;
- ret = sys32_ioctl(fd, cmd, arg);
+ ret = compat_ioctl(fd, cmd, arg);
__get_user(newval, ptr);
if(newval == -1) {
__put_user(oldval, ptr);
@@ -277,7 +277,7 @@
}
};

- ret = sys32_ioctl(fd, cmd, arg);
+ ret = compat_ioctl(fd, cmd, arg);
/* so stupid... */
ret = (ret == -EINVAL ? -EOPNOTSUPP : ret);
out:
diff -ur clean/arch/sparc64/kernel/systbls.S linux-2.5.62/arch/sparc64/kernel/systbls.S
--- clean/arch/sparc64/kernel/systbls.S 2003-02-17 23:56:13.000000000 +0100
+++ linux-2.5.62/arch/sparc64/kernel/systbls.S 2003-02-20 22:18:29.000000000 +0100
@@ -29,7 +29,7 @@
.word sys_chown, sys_sync, sys_kill, compat_sys_newstat, sys32_sendfile
/*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid
.word sys_umount, sys32_setgid16, sys32_getgid16, sys_signal, sys32_geteuid16
-/*50*/ .word sys32_getegid16, sys_acct, sys_nis_syscall, sys_getgid, sys32_ioctl
+/*50*/ .word sys32_getegid16, sys_acct, sys_nis_syscall, sys_getgid, compat_ioctl
.word sys_reboot, sys32_mmap2, sys_symlink, sys_readlink, sys32_execve
/*60*/ .word sys_umask, sys_chroot, compat_sys_newfstat, sys_fstat64, sys_getpagesize
.word sys_msync, sys_vfork, sys32_pread64, sys32_pwrite64, sys_geteuid
diff -ur clean/arch/x86_64/ia32/ia32_ioctl.c linux-2.5.62/arch/x86_64/ia32/ia32_ioctl.c
--- clean/arch/x86_64/ia32/ia32_ioctl.c 2003-02-17 23:56:15.000000000 +0100
+++ linux-2.5.62/arch/x86_64/ia32/ia32_ioctl.c 2003-02-20 22:18:30.000000000 +0100
@@ -680,7 +680,7 @@
return (void *)regs->rsp - len;
}

-static int siocdevprivate_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+int siocdevprivate_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct ifreq *u_ifreq64;
struct ifreq32 *u_ifreq32 = (struct ifreq32 *) arg;
@@ -3578,18 +3578,12 @@
return err;
}

-struct ioctl_trans {
- unsigned long cmd;
- int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
- struct ioctl_trans *next;
-};
-
#define REF_SYMBOL(handler) if (0) (void)handler;
#define HANDLE_IOCTL2(cmd,handler) REF_SYMBOL(handler); asm volatile(".quad %c0, " #handler ",0"::"i" (cmd));
#define HANDLE_IOCTL(cmd,handler) HANDLE_IOCTL2(cmd,handler)
#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL(cmd,sys_ioctl)
-#define IOCTL_TABLE_START void ioctl_dummy(void) { asm volatile("\nioctl_start:\n\t" );
-#define IOCTL_TABLE_END asm volatile("\nioctl_end:"); }
+#define IOCTL_TABLE_START void ioctl_dummy(void) { asm volatile("\n.global ioctl_start\nioctl_start:\n\t" );
+#define IOCTL_TABLE_END asm volatile("\n.global ioctl_end;\nioctl_end:\n"); }

IOCTL_TABLE_START
/* List here explicitly which ioctl's are known to have
@@ -4432,217 +4426,3 @@
HANDLE_IOCTL(MTRRIOC32_KILL_PAGE_ENTRY, mtrr_ioctl32)
IOCTL_TABLE_END

-#define IOCTL_HASHSIZE 256
-struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
-
-extern struct ioctl_trans ioctl_start[], ioctl_end[];
-
-extern struct ioctl_trans ioctl_start[], ioctl_end[];
-
-static inline unsigned long ioctl32_hash(unsigned long cmd)
-{
- return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
-}
-
-static void ioctl32_insert_translation(struct ioctl_trans *trans)
-{
- unsigned long hash;
- struct ioctl_trans *t;
-
- hash = ioctl32_hash (trans->cmd);
- if (!ioctl32_hash_table[hash])
- ioctl32_hash_table[hash] = trans;
- else {
- t = ioctl32_hash_table[hash];
- while (t->next)
- t = t->next;
- trans->next = 0;
- t->next = trans;
- }
-}
-
-static int __init init_sys32_ioctl(void)
-{
- int i;
-
- for (i = 0; &ioctl_start[i] < &ioctl_end[0]; i++) {
- if (ioctl_start[i].next != 0) {
- printk("ioctl translation %d bad\n",i);
- return -1;
- }
-
- ioctl32_insert_translation(&ioctl_start[i]);
- }
- return 0;
-}
-
-__initcall(init_sys32_ioctl);
-
-static struct ioctl_trans *ioctl_free_list;
-
-/* Never free them really. This avoids SMP races. With a Read-Copy-Update
- enabled kernel we could just use the RCU infrastructure for this. */
-static void free_ioctl(struct ioctl_trans *t)
-{
- t->cmd = 0;
- mb();
- t->next = ioctl_free_list;
- ioctl_free_list = t;
-}
-
-int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
-{
- struct ioctl_trans *t;
- unsigned long hash = ioctl32_hash(cmd);
-
- lock_kernel();
- for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
- t;
- t = t->next) {
- if (t->cmd == cmd) {
- printk("Trying to register duplicated ioctl32 handler %x\n", cmd);
- unlock_kernel();
- return -EINVAL;
- }
- }
-
- if (ioctl_free_list) {
- t = ioctl_free_list;
- ioctl_free_list = t->next;
- } else {
- t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL);
- if (!t) {
- unlock_kernel();
- return -ENOMEM;
- }
- }
-
- t->next = NULL;
- t->cmd = cmd;
- t->handler = handler;
- ioctl32_insert_translation(t);
-
- unlock_kernel();
- return 0;
-}
-
-static inline int builtin_ioctl(struct ioctl_trans *t)
-{
- return t >= (struct ioctl_trans *)ioctl_start &&
- t < (struct ioctl_trans *)ioctl_end;
-}
-
-/* Problem:
- This function cannot unregister duplicate ioctls, because they are not
- unique.
- When they happen we need to extend the prototype to pass the handler too. */
-
-int unregister_ioctl32_conversion(unsigned int cmd)
-{
- unsigned long hash = ioctl32_hash(cmd);
- struct ioctl_trans *t, *t1;
-
- lock_kernel();
-
- t = (struct ioctl_trans *)ioctl32_hash_table[hash];
- if (!t) {
- unlock_kernel();
- return -EINVAL;
- }
-
- if (t->cmd == cmd) {
- if (builtin_ioctl(t)) {
- printk("%p tried to unregister builtin ioctl %x\n",
- __builtin_return_address(0), cmd);
- } else {
- ioctl32_hash_table[hash] = t->next;
- free_ioctl(t);
- unlock_kernel();
- return 0;
- }
- }
- while (t->next) {
- t1 = (struct ioctl_trans *)(long)t->next;
- if (t1->cmd == cmd) {
- if (builtin_ioctl(t1)) {
- printk("%p tried to unregister builtin ioctl %x\n",
- __builtin_return_address(0), cmd);
- goto out;
- } else {
- t->next = t1->next;
- free_ioctl(t1);
- unlock_kernel();
- return 0;
- }
- }
- t = t1;
- }
- printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd);
- out:
- unlock_kernel();
- return -EINVAL;
-}
-
-EXPORT_SYMBOL(register_ioctl32_conversion);
-EXPORT_SYMBOL(unregister_ioctl32_conversion);
-
-asmlinkage long sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- struct file * filp;
- int error = -EBADF;
- int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
- 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;
- }
-
- t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)];
-
- while (t && t->cmd != cmd)
- t = (struct ioctl_trans *)t->next;
- if (t) {
- handler = t->handler;
- error = handler(fd, cmd, arg, filp);
- } else if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
- error = siocdevprivate_ioctl(fd, cmd, arg);
- } else {
- static int count;
- if (++count <= 50) {
- char buf[10];
- char *path = (char *)__get_free_page(GFP_KERNEL), *fn = "?";
-
- /* find the name of the device. */
- if (path) {
- struct file *f = fget(fd);
- if (f) {
- fn = d_path(f->f_dentry, f->f_vfsmnt,
- path, PAGE_SIZE);
- fput(f);
- }
- }
-
- sprintf(buf,"'%c'", (cmd>>24) & 0x3f);
- if (!isprint(buf[1]))
- sprintf(buf, "%02x", buf[1]);
- printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
- "cmd(%08x){%s} arg(%08x) on %s\n",
- current->comm, current->pid,
- (int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
- fn);
- if (path)
- free_page((unsigned long)path);
- }
- error = -EINVAL;
- }
-out:
- fput(filp);
-out2:
- return error;
-}
-
diff -ur clean/arch/x86_64/ia32/ia32entry.S linux-2.5.62/arch/x86_64/ia32/ia32entry.S
--- clean/arch/x86_64/ia32/ia32entry.S 2003-02-17 23:56:14.000000000 +0100
+++ linux-2.5.62/arch/x86_64/ia32/ia32entry.S 2003-02-20 22:18:30.000000000 +0100
@@ -254,7 +254,7 @@
.quad sys_acct
.quad sys_umount /* new_umount */
.quad ni_syscall /* old lock syscall holder */
- .quad sys32_ioctl
+ .quad compat_ioctl
.quad sys32_fcntl64 /* 55 */
.quad ni_syscall /* old mpx syscall holder */
.quad sys_setpgid
diff -ur clean/include/linux/ioctl32.h linux-2.5.62/include/linux/ioctl32.h
--- clean/include/linux/ioctl32.h 2003-02-17 23:55:50.000000000 +0100
+++ linux-2.5.62/include/linux/ioctl32.h 2003-02-20 22:18:30.000000000 +0100
@@ -19,5 +19,10 @@

extern int unregister_ioctl32_conversion(unsigned int cmd);

+struct ioctl_trans {
+ unsigned long cmd;
+ int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
+ struct ioctl_trans *next;
+};

#endif
diff -ur clean/kernel/compat.c linux-2.5.62/kernel/compat.c
--- clean/kernel/compat.c 2003-02-17 23:56:15.000000000 +0100
+++ linux-2.5.62/kernel/compat.c 2003-02-20 22:18:30.000000000 +0100
@@ -18,6 +18,12 @@
#include <linux/signal.h>
#include <linux/sched.h> /* for MAX_SCHEDULE_TIMEOUT */
#include <linux/futex.h> /* for FUTEX_WAIT */
+#include <linux/ioctl32.h>
+#include <linux/init.h>
+#include <linux/sockios.h> /* for SIOCDEVPRIVATE */
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>

#include <asm/uaccess.h>

@@ -226,3 +232,219 @@
}
return do_futex((unsigned long)uaddr, op, val, timeout);
}
+
+/* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64 */
+
+#define IOCTL_HASHSIZE 256
+struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
+
+extern struct ioctl_trans ioctl_start[], ioctl_end[];
+
+static inline unsigned long ioctl32_hash(unsigned long cmd)
+{
+ return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
+}
+
+static void ioctl32_insert_translation(struct ioctl_trans *trans)
+{
+ unsigned long hash;
+ struct ioctl_trans *t;
+
+ hash = ioctl32_hash (trans->cmd);
+ if (!ioctl32_hash_table[hash])
+ ioctl32_hash_table[hash] = trans;
+ else {
+ t = ioctl32_hash_table[hash];
+ while (t->next)
+ t = t->next;
+ trans->next = 0;
+ t->next = trans;
+ }
+}
+
+static int __init init_sys32_ioctl(void)
+{
+ int i;
+
+ for (i = 0; &ioctl_start[i] < &ioctl_end[0]; i++) {
+ if (ioctl_start[i].next != 0) {
+ printk("ioctl translation %d bad\n",i);
+ return -1;
+ }
+
+ ioctl32_insert_translation(&ioctl_start[i]);
+ }
+ return 0;
+}
+
+__initcall(init_sys32_ioctl);
+
+static struct ioctl_trans *ioctl_free_list;
+
+/* Never free them really. This avoids SMP races. With a Read-Copy-Update
+ enabled kernel we could just use the RCU infrastructure for this. */
+static void free_ioctl(struct ioctl_trans *t)
+{
+ t->cmd = 0;
+ mb();
+ t->next = ioctl_free_list;
+ ioctl_free_list = t;
+}
+
+int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
+{
+ struct ioctl_trans *t;
+ unsigned long hash = ioctl32_hash(cmd);
+
+ lock_kernel();
+ for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
+ t;
+ t = t->next) {
+ if (t->cmd == cmd) {
+ printk("Trying to register duplicated ioctl32 handler %x\n", cmd);
+ unlock_kernel();
+ return -EINVAL;
+ }
+ }
+
+ if (ioctl_free_list) {
+ t = ioctl_free_list;
+ ioctl_free_list = t->next;
+ } else {
+ t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL);
+ if (!t) {
+ unlock_kernel();
+ return -ENOMEM;
+ }
+ }
+
+ t->next = NULL;
+ t->cmd = cmd;
+ t->handler = handler;
+ ioctl32_insert_translation(t);
+
+ unlock_kernel();
+ return 0;
+}
+
+static inline int builtin_ioctl(struct ioctl_trans *t)
+{
+ return t >= (struct ioctl_trans *)ioctl_start &&
+ t < (struct ioctl_trans *)ioctl_end;
+}
+
+/* Problem:
+ This function cannot unregister duplicate ioctls, because they are not
+ unique.
+ When they happen we need to extend the prototype to pass the handler too. */
+
+int unregister_ioctl32_conversion(unsigned int cmd)
+{
+ unsigned long hash = ioctl32_hash(cmd);
+ struct ioctl_trans *t, *t1;
+
+ lock_kernel();
+
+ t = (struct ioctl_trans *)ioctl32_hash_table[hash];
+ if (!t) {
+ unlock_kernel();
+ return -EINVAL;
+ }
+
+ if (t->cmd == cmd) {
+ if (builtin_ioctl(t)) {
+ printk("%p tried to unregister builtin ioctl %x\n",
+ __builtin_return_address(0), cmd);
+ } else {
+ ioctl32_hash_table[hash] = t->next;
+ free_ioctl(t);
+ unlock_kernel();
+ return 0;
+ }
+ }
+ while (t->next) {
+ t1 = (struct ioctl_trans *)(long)t->next;
+ if (t1->cmd == cmd) {
+ if (builtin_ioctl(t1)) {
+ printk("%p tried to unregister builtin ioctl %x\n",
+ __builtin_return_address(0), cmd);
+ goto out;
+ } else {
+ t->next = t1->next;
+ free_ioctl(t1);
+ unlock_kernel();
+ return 0;
+ }
+ }
+ t = t1;
+ }
+ printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd);
+ out:
+ unlock_kernel();
+ return -EINVAL;
+}
+
+EXPORT_SYMBOL(register_ioctl32_conversion);
+EXPORT_SYMBOL(unregister_ioctl32_conversion);
+
+asmlinkage long compat_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+ int error = -EBADF;
+ int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
+ 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;
+ }
+
+ t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)];
+
+ while (t && t->cmd != cmd)
+ t = (struct ioctl_trans *)t->next;
+ if (t) {
+ handler = t->handler;
+ error = handler(fd, cmd, arg, filp);
+ } else if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
+ error = siocdevprivate_ioctl(fd, cmd, arg);
+ } else {
+ static int count;
+ if (++count <= 50) {
+ char buf[10];
+ char *path = (char *)__get_free_page(GFP_KERNEL), *fn = "?";
+
+ /* find the name of the device. */
+ if (path) {
+ struct file *f = fget(fd);
+ if (f) {
+ fn = d_path(f->f_dentry, f->f_vfsmnt,
+ path, PAGE_SIZE);
+ fput(f);
+ }
+ }
+
+ sprintf(buf,"'%c'", (cmd>>24) & 0x3f);
+ if (!isprint(buf[1]))
+ sprintf(buf, "%02x", buf[1]);
+ printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
+ "cmd(%08x){%s} arg(%08x) on %s\n",
+ current->comm, current->pid,
+ (int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
+ fn);
+ if (path)
+ free_page((unsigned long)path);
+ }
+ error = -EINVAL;
+ }
+out:
+ fput(filp);
+out2:
+ return error;
+}
+
+



--
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]


2003-02-20 22:34:37

by Jeff Garzik

[permalink] [raw]
Subject: Re: ioctl32 consolidation

On Thu, Feb 20, 2003 at 11:31:19PM +0100, Pavel Machek wrote:
> Currently, 32-bit emulation in kernel has *5* copies, and its >1000
> lines each.

Yes :/ Consolidating all these copies into a single layer has been a
"project to be" for quite some time.

I do not know if it is too late in 2.5.x to begin this work, however.
We _are_ in a feature freeze... I suppose it is up to the consensus of
arch maintainers, because it [obviously] does not affect ia32.

Jeff



2003-02-20 22:46:44

by Max Krasnyansky

[permalink] [raw]
Subject: Re: ioctl32 consolidation

At 02:31 PM 2/20/2003, Pavel Machek wrote:
>Hi!
>
>Currently, 32-bit emulation in kernel has *5* copies, and its >1000
>lines each. Plus, locking of all but x86-64 architectures is broken
>(I'm told by andi ;-).
>
>So, here's patch that starts sharing sys32_ioctl() [as a first step],
>which should rmove locking problems.
>
>I've done the work for x86-64 and sparc64; if it looks good I'll
>attempt to do other architectures. [Unless maintainers prefer to do it
>themselves: I don't have easy access to 64-bit machines besides
>hammer.]

Nice. I'm glad that somebody started looking at this (I was going to work
on that but didn't have time for it).
Patch looks ok to me.

Eventually we'll be able to kill ugly mess like arch/sparc64/kernel/ioctl32.c.
That stuff really belongs to the actual subsystems that implement those ioctls.

Thanks
Max

2003-02-20 23:00:10

by Jeff Garzik

[permalink] [raw]
Subject: Re: ioctl32 consolidation

On Thu, Feb 20, 2003 at 02:56:22PM -0800, Max Krasnyansky wrote:
> Eventually we'll be able to kill ugly mess like arch/sparc64/kernel/ioctl32.c.
> That stuff really belongs to the actual subsystems that implement those ioctls.

Yes.

Note that ALSA is already doing it right: sound/core/ioctl32/*

Jeff



2003-02-21 00:45:59

by David Miller

[permalink] [raw]
Subject: Re: ioctl32 consolidation

From: Max Krasnyansky <[email protected]>
Date: Thu, 20 Feb 2003 14:56:22 -0800

Eventually we'll be able to kill ugly mess like arch/sparc64/kernel/ioctl32.c.
That stuff really belongs to the actual subsystems that implement those ioctls.

Not really possible with things like SIOCDEVPRIVATE...
Those need special processing and even that is insufficient.

2003-02-21 01:21:54

by Max Krasnyansky

[permalink] [raw]
Subject: Re: ioctl32 consolidation

At 04:36 PM 2/20/2003, David S. Miller wrote:
> From: Max Krasnyansky <[email protected]>
> Date: Thu, 20 Feb 2003 14:56:22 -0800
>
> Eventually we'll be able to kill ugly mess like arch/sparc64/kernel/ioctl32.c.
> That stuff really belongs to the actual subsystems that implement those ioctls.
>
>Not really possible with things like SIOCDEVPRIVATE...
>Those need special processing and even that is insufficient.
Hmm. It seems to that all you need for SIOCDEVPRIVATE is ability to register
ranges of ioctls.
i.e. something like this
int register_ioctl32_conversion_rage(uint start, uint end, handler);

net/core/dev.c
register_ioctl32_conversion_range(SIOCDEVPRIVATE, SIOCDEVPRIVATE + 15, siocdevprivate_ioctl);

Am I missing something here ?

Max

2003-02-21 01:30:34

by Jeff Garzik

[permalink] [raw]
Subject: Re: ioctl32 consolidation

Max Krasnyansky wrote:
> Hmm. It seems to that all you need for SIOCDEVPRIVATE is ability to register
> ranges of ioctls.
> i.e. something like this
> int register_ioctl32_conversion_rage(uint start, uint end, handler);
>
> net/core/dev.c
> register_ioctl32_conversion_range(SIOCDEVPRIVATE, SIOCDEVPRIVATE + 15, siocdevprivate_ioctl);
>
> Am I missing something here ?


Yes. Think about the name of the ioctl :)

It is impossible for generic arch code to implement support for
driver-private ioctls, because these naturally differ between each driver.

Jeff



2003-02-21 03:26:28

by David Miller

[permalink] [raw]
Subject: Re: ioctl32 consolidation

From: Max Krasnyansky <[email protected]>
Date: Thu, 20 Feb 2003 17:31:43 -0800

Am I missing something here ?

The data type for things passed into SIOCDEVPRIVATE is
totally opaque.

Multiple drivers, passing in different kinds of data,
use SIOCDEVPRIVATE. It is inherenly untranslatable
at the point where we are at sys_ioctl32().

2003-02-21 11:24:24

by Pavel Machek

[permalink] [raw]
Subject: Re: ioctl32 consolidation

Hi!


> > Currently, 32-bit emulation in kernel has *5* copies, and its >1000
> > lines each.
>
> Yes :/ Consolidating all these copies into a single layer has been a
> "project to be" for quite some time.
>
> I do not know if it is too late in 2.5.x to begin this work, however.
> We _are_ in a feature freeze... I suppose it is up to the consensus of
> arch maintainers, because it [obviously] does not affect ia32.

Actually Andi asked me to do the work. Dave, is it okay with you? What
about other maintainers?
Pavel
--
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.

2003-02-21 11:48:00

by Andi Kleen

[permalink] [raw]
Subject: Re: ioctl32 consolidation

On Fri, Feb 21, 2003 at 12:34:29PM +0100, Pavel Machek wrote:
> Hi!
>
>
> > > Currently, 32-bit emulation in kernel has *5* copies, and its >1000
> > > lines each.
> >
> > Yes :/ Consolidating all these copies into a single layer has been a
> > "project to be" for quite some time.
> >
> > I do not know if it is too late in 2.5.x to begin this work, however.
> > We _are_ in a feature freeze... I suppose it is up to the consensus of
> > arch maintainers, because it [obviously] does not affect ia32.
>
> Actually Andi asked me to do the work. Dave, is it okay with you? What
> about other maintainers?

One issue you need to be careful about is that long long has different
alignment between 32bit and 64bit on ia64 and x86-64. On sparc64/mips64/ppc64
etc. that isn't the case. The x86-64 handlers convert sometimes more than
the later ones because of that.

Also some ioctl handlers are endian dependent, at least in x86-64 (I think
I commented them all). Not sure if Dave did the same.

First step probably is to just get register_ioctl32_translation into
a common header and implementation file.

-Andi

2003-02-21 22:19:42

by Arnd Bergmann

[permalink] [raw]
Subject: Re: ioctl32 consolidation

Pavel Machek wrote:

>> I do not know if it is too late in 2.5.x to begin this work, however.
>> We _are_ in a feature freeze... I suppose it is up to the consensus of
>> arch maintainers, because it [obviously] does not affect ia32.
>
> Actually Andi asked me to do the work. Dave, is it okay with you? What
> about other maintainers?

For s390, I'd love to see progress in the consolidation. Feel free to
submit changes for arch/s390x/kernel/ioctl32.c directly, like
Stephen Rothwell does for the syscall32 consolidation. Of course,
Martin has the last word here, but I'm rather sure he agress with me
in this.

If you want access to an s390x system, you can probably get access
to one at http://www-1.ibm.com/servers/eserver/zseries/os/linux/lcds/
or install the hercules emulator. I try to keep working kernel tree
at http://linux-390.bkbits.net/, but the 32 bit emulation has been
broken for most of 2.5.

Note that for any ioctls that pass pointers, you will need special
massaging for the high order bit of the user space pointer, because
s390 only has 31 bit pointers, not 32 bit.

Arnd <><

2003-02-21 23:27:52

by David Miller

[permalink] [raw]
Subject: Re: ioctl32 consolidation

From: Pavel Machek <[email protected]>
Date: Fri, 21 Feb 2003 12:34:29 +0100

> I do not know if it is too late in 2.5.x to begin this work, however.
> We _are_ in a feature freeze... I suppose it is up to the consensus of
> arch maintainers, because it [obviously] does not affect ia32.

Actually Andi asked me to do the work. Dave, is it okay with you? What
about other maintainers?

I'm totally fine with it.

2003-02-24 08:57:07

by Martin Schwidefsky

[permalink] [raw]
Subject: Re: ioctl32 consolidation


> For s390, I'd love to see progress in the consolidation. Feel free to
> submit changes for arch/s390x/kernel/ioctl32.c directly, like
> Stephen Rothwell does for the syscall32 consolidation. Of course,
> Martin has the last word here, but I'm rather sure he agress with me
> in this.
Everything that moves out of arch/s390x/kernel/ioctl32.c has my blessing.
I am currently working on the 31 bit emulation. It almost works again and
I will include the changes in the next patch set.

blue skies,
Martin



2003-03-01 20:00:03

by Anton Blanchard

[permalink] [raw]
Subject: Re: ioctl32 consolidation


> Actually Andi asked me to do the work. Dave, is it okay with you? What
> about other maintainers?

>From a ppc64 perspective Im happy for this to be done ASAP :)

Anton