Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+44 -46
+44 -46
fs/compat_ioctl.c
··· 111 111 #include <linux/dvb/frontend.h> 112 112 #include <linux/dvb/video.h> 113 113 114 + #include <linux/sort.h> 115 + 114 116 #ifdef CONFIG_SPARC 115 117 #include <asm/fbio.h> 116 118 #endif ··· 1050 1048 } 1051 1049 #endif 1052 1050 1051 + /* 1052 + * simple reversible transform to make our table more evenly 1053 + * distributed after sorting. 1054 + */ 1055 + #define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff) 1053 1056 1054 - struct ioctl_trans { 1055 - unsigned long cmd; 1056 - struct ioctl_trans *next; 1057 - }; 1058 - 1059 - /* pointer to compatible structure or no argument */ 1060 - #define COMPATIBLE_IOCTL(cmd) { (cmd), }, 1061 - 1057 + #define COMPATIBLE_IOCTL(cmd) XFORM(cmd), 1062 1058 /* ioctl should not be warned about even if it's not implemented. 1063 1059 Valid reasons to use this: 1064 1060 - It is implemented with ->compat_ioctl on some device, but programs ··· 1066 1066 Most other reasons are not valid. */ 1067 1067 #define IGNORE_IOCTL(cmd) COMPATIBLE_IOCTL(cmd) 1068 1068 1069 - static struct ioctl_trans ioctl_start[] = { 1069 + static unsigned int ioctl_pointer[] = { 1070 1070 /* compatible ioctls first */ 1071 1071 COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */ 1072 1072 COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */ ··· 1710 1710 #endif 1711 1711 }; 1712 1712 1713 - #define IOCTL_HASHSIZE 256 1714 - static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; 1715 - 1716 - static inline unsigned long ioctl32_hash(unsigned long cmd) 1717 - { 1718 - return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE; 1719 - } 1720 - 1721 1713 /* 1722 1714 * Convert common ioctl arguments based on their command number 1723 1715 * ··· 1853 1861 free_page((unsigned long)path); 1854 1862 } 1855 1863 1864 + static int compat_ioctl_check_table(unsigned int xcmd) 1865 + { 1866 + int i; 1867 + const int max = ARRAY_SIZE(ioctl_pointer) - 1; 1868 + 1869 + BUILD_BUG_ON(max >= (1 << 16)); 1870 + 1871 + /* guess initial offset into table, assuming a 1872 + normalized distribution */ 1873 + i = ((xcmd >> 16) * max) >> 16; 1874 + 1875 + /* do linear search up first, until greater or equal */ 1876 + while (ioctl_pointer[i] < xcmd && i < max) 1877 + i++; 1878 + 1879 + /* then do linear search down */ 1880 + while (ioctl_pointer[i] > xcmd && i > 0) 1881 + i--; 1882 + 1883 + return ioctl_pointer[i] == xcmd; 1884 + } 1885 + 1856 1886 asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, 1857 1887 unsigned long arg) 1858 1888 { 1859 1889 struct file *filp; 1860 1890 int error = -EBADF; 1861 - struct ioctl_trans *t; 1862 1891 int fput_needed; 1863 1892 1864 1893 filp = fget_light(fd, &fput_needed); ··· 1936 1923 break; 1937 1924 } 1938 1925 1939 - for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) { 1940 - if (t->cmd == cmd) 1941 - goto found_handler; 1942 - } 1926 + if (compat_ioctl_check_table(XFORM(cmd))) 1927 + goto found_handler; 1943 1928 1944 1929 error = do_ioctl_trans(fd, cmd, arg, filp); 1945 1930 if (error == -ENOIOCTLCMD) { ··· 1960 1949 return error; 1961 1950 } 1962 1951 1963 - static void ioctl32_insert_translation(struct ioctl_trans *trans) 1952 + static int __init init_sys32_ioctl_cmp(const void *p, const void *q) 1964 1953 { 1965 - unsigned long hash; 1966 - struct ioctl_trans *t; 1967 - 1968 - hash = ioctl32_hash (trans->cmd); 1969 - if (!ioctl32_hash_table[hash]) 1970 - ioctl32_hash_table[hash] = trans; 1971 - else { 1972 - t = ioctl32_hash_table[hash]; 1973 - while (t->next) 1974 - t = t->next; 1975 - trans->next = NULL; 1976 - t->next = trans; 1977 - } 1954 + unsigned int a, b; 1955 + a = *(unsigned int *)p; 1956 + b = *(unsigned int *)q; 1957 + if (a > b) 1958 + return 1; 1959 + if (a < b) 1960 + return -1; 1961 + return 0; 1978 1962 } 1979 1963 1980 1964 static int __init init_sys32_ioctl(void) 1981 1965 { 1982 - int i; 1983 - 1984 - for (i = 0; i < ARRAY_SIZE(ioctl_start); i++) { 1985 - if (ioctl_start[i].next) { 1986 - printk("ioctl translation %d bad\n",i); 1987 - return -1; 1988 - } 1989 - 1990 - ioctl32_insert_translation(&ioctl_start[i]); 1991 - } 1966 + sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer), 1967 + init_sys32_ioctl_cmp, NULL); 1992 1968 return 0; 1993 1969 } 1994 1970 __initcall(init_sys32_ioctl);