aboutsummaryrefslogtreecommitdiffstats
path: root/fs/compat_ioctl.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2009-11-05 13:52:55 -0500
committerArnd Bergmann <arnd@arndb.de>2009-12-10 16:52:11 -0500
commit661f627da98c0647bcc002ef35e5441fb3ce667c (patch)
tree0820ff0d1734edb252c0073babf7806614a8fa55 /fs/compat_ioctl.c
parent789f0f89118a80a3ff5309371e5820f623ed2a53 (diff)
compat_ioctl: simplify lookup table
The compat_ioctl table now only contains entries for COMPATIBLE_IOCTL, so we only need to know if a number is listed in it or now. As an optimization, we hash the table entries with a reversible transformation to get a more uniform distribution over it, sort the table at startup and then guess the position in the table when an ioctl number gets called to do a linear search from there. With the current set of ioctl numbers and the chosen transformation function, we need an average of four steps to find if a number is in the set, all of the accesses within one or two cache lines. This at least as good as the previous hash table approach but saves 8.5 kb of kernel memory. Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'fs/compat_ioctl.c')
-rw-r--r--fs/compat_ioctl.c90
1 files changed, 44 insertions, 46 deletions
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 7895bdb0c304..b4873ae84ca1 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -111,6 +111,8 @@
111#include <linux/dvb/frontend.h> 111#include <linux/dvb/frontend.h>
112#include <linux/dvb/video.h> 112#include <linux/dvb/video.h>
113 113
114#include <linux/sort.h>
115
114#ifdef CONFIG_SPARC 116#ifdef CONFIG_SPARC
115#include <asm/fbio.h> 117#include <asm/fbio.h>
116#endif 118#endif
@@ -1048,15 +1050,13 @@ static int compat_ioctl_preallocate(struct file *file, unsigned long arg)
1048} 1050}
1049#endif 1051#endif
1050 1052
1053/*
1054 * simple reversible transform to make our table more evenly
1055 * distributed after sorting.
1056 */
1057#define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff)
1051 1058
1052struct ioctl_trans { 1059#define COMPATIBLE_IOCTL(cmd) XFORM(cmd),
1053 unsigned long cmd;
1054 struct ioctl_trans *next;
1055};
1056
1057/* pointer to compatible structure or no argument */
1058#define COMPATIBLE_IOCTL(cmd) { (cmd), },
1059
1060/* ioctl should not be warned about even if it's not implemented. 1060/* ioctl should not be warned about even if it's not implemented.
1061 Valid reasons to use this: 1061 Valid reasons to use this:
1062 - It is implemented with ->compat_ioctl on some device, but programs 1062 - It is implemented with ->compat_ioctl on some device, but programs
@@ -1066,7 +1066,7 @@ struct ioctl_trans {
1066 Most other reasons are not valid. */ 1066 Most other reasons are not valid. */
1067#define IGNORE_IOCTL(cmd) COMPATIBLE_IOCTL(cmd) 1067#define IGNORE_IOCTL(cmd) COMPATIBLE_IOCTL(cmd)
1068 1068
1069static struct ioctl_trans ioctl_start[] = { 1069static unsigned int ioctl_pointer[] = {
1070/* compatible ioctls first */ 1070/* compatible ioctls first */
1071COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */ 1071COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */
1072COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */ 1072COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */
@@ -1710,14 +1710,6 @@ IGNORE_IOCTL(FBIOGCURSOR32)
1710#endif 1710#endif
1711}; 1711};
1712 1712
1713#define IOCTL_HASHSIZE 256
1714static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
1715
1716static inline unsigned long ioctl32_hash(unsigned long cmd)
1717{
1718 return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
1719}
1720
1721/* 1713/*
1722 * Convert common ioctl arguments based on their command number 1714 * Convert common ioctl arguments based on their command number
1723 * 1715 *
@@ -1861,12 +1853,33 @@ static void compat_ioctl_error(struct file *filp, unsigned int fd,
1861 free_page((unsigned long)path); 1853 free_page((unsigned long)path);
1862} 1854}
1863 1855
1856static int compat_ioctl_check_table(unsigned int xcmd)
1857{
1858 int i;
1859 const int max = ARRAY_SIZE(ioctl_pointer) - 1;
1860
1861 BUILD_BUG_ON(max >= (1 << 16));
1862
1863 /* guess initial offset into table, assuming a
1864 normalized distribution */
1865 i = ((xcmd >> 16) * max) >> 16;
1866
1867 /* do linear search up first, until greater or equal */
1868 while (ioctl_pointer[i] < xcmd && i < max)
1869 i++;
1870
1871 /* then do linear search down */
1872 while (ioctl_pointer[i] > xcmd && i > 0)
1873 i--;
1874
1875 return ioctl_pointer[i] == xcmd;
1876}
1877
1864asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, 1878asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
1865 unsigned long arg) 1879 unsigned long arg)
1866{ 1880{
1867 struct file *filp; 1881 struct file *filp;
1868 int error = -EBADF; 1882 int error = -EBADF;
1869 struct ioctl_trans *t;
1870 int fput_needed; 1883 int fput_needed;
1871 1884
1872 filp = fget_light(fd, &fput_needed); 1885 filp = fget_light(fd, &fput_needed);
@@ -1923,10 +1936,8 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
1923 break; 1936 break;
1924 } 1937 }
1925 1938
1926 for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) { 1939 if (compat_ioctl_check_table(XFORM(cmd)))
1927 if (t->cmd == cmd) 1940 goto found_handler;
1928 goto found_handler;
1929 }
1930 1941
1931 error = do_ioctl_trans(fd, cmd, arg, filp); 1942 error = do_ioctl_trans(fd, cmd, arg, filp);
1932 if (error == -ENOIOCTLCMD) { 1943 if (error == -ENOIOCTLCMD) {
@@ -1949,35 +1960,22 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
1949 return error; 1960 return error;
1950} 1961}
1951 1962
1952static void ioctl32_insert_translation(struct ioctl_trans *trans) 1963static int __init init_sys32_ioctl_cmp(const void *p, const void *q)
1953{ 1964{
1954 unsigned long hash; 1965 unsigned int a, b;
1955 struct ioctl_trans *t; 1966 a = *(unsigned int *)p;
1956 1967 b = *(unsigned int *)q;
1957 hash = ioctl32_hash (trans->cmd); 1968 if (a > b)
1958 if (!ioctl32_hash_table[hash]) 1969 return 1;
1959 ioctl32_hash_table[hash] = trans; 1970 if (a < b)
1960 else { 1971 return -1;
1961 t = ioctl32_hash_table[hash]; 1972 return 0;
1962 while (t->next)
1963 t = t->next;
1964 trans->next = NULL;
1965 t->next = trans;
1966 }
1967} 1973}
1968 1974
1969static int __init init_sys32_ioctl(void) 1975static int __init init_sys32_ioctl(void)
1970{ 1976{
1971 int i; 1977 sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer),
1972 1978 init_sys32_ioctl_cmp, NULL);
1973 for (i = 0; i < ARRAY_SIZE(ioctl_start); i++) {
1974 if (ioctl_start[i].next) {
1975 printk("ioctl translation %d bad\n",i);
1976 return -1;
1977 }
1978
1979 ioctl32_insert_translation(&ioctl_start[i]);
1980 }
1981 return 0; 1979 return 0;
1982} 1980}
1983__initcall(init_sys32_ioctl); 1981__initcall(init_sys32_ioctl);