Commit 661f627da98c0647bcc002ef35e5441fb3ce667c

Authored by Arnd Bergmann
1 parent 789f0f8911

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>

Showing 1 changed file with 44 additions and 46 deletions Side-by-side Diff

... ... @@ -111,6 +111,8 @@
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
117 119  
... ... @@ -1048,15 +1050,13 @@
1048 1050 }
1049 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  
1052   -struct ioctl_trans {
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   -
  1059 +#define COMPATIBLE_IOCTL(cmd) XFORM(cmd),
1060 1060 /* ioctl should not be warned about even if it's not implemented.
1061 1061 Valid reasons to use this:
1062 1062 - It is implemented with ->compat_ioctl on some device, but programs
... ... @@ -1066,7 +1066,7 @@
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,14 +1710,6 @@
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 *
1724 1716  
... ... @@ -1861,12 +1853,33 @@
1861 1853 free_page((unsigned long)path);
1862 1854 }
1863 1855  
  1856 +static 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 +
1864 1878 asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
1865 1879 unsigned long arg)
1866 1880 {
1867 1881 struct file *filp;
1868 1882 int error = -EBADF;
1869   - struct ioctl_trans *t;
1870 1883 int fput_needed;
1871 1884  
1872 1885 filp = fget_light(fd, &fput_needed);
... ... @@ -1923,10 +1936,8 @@
1923 1936 break;
1924 1937 }
1925 1938  
1926   - for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) {
1927   - if (t->cmd == cmd)
1928   - goto found_handler;
1929   - }
  1939 + if (compat_ioctl_check_table(XFORM(cmd)))
  1940 + goto found_handler;
1930 1941  
1931 1942 error = do_ioctl_trans(fd, cmd, arg, filp);
1932 1943 if (error == -ENOIOCTLCMD) {
1933 1944  
1934 1945  
... ... @@ -1949,35 +1960,22 @@
1949 1960 return error;
1950 1961 }
1951 1962  
1952   -static void ioctl32_insert_translation(struct ioctl_trans *trans)
  1963 +static int __init init_sys32_ioctl_cmp(const void *p, const void *q)
1953 1964 {
1954   - unsigned long hash;
1955   - struct ioctl_trans *t;
1956   -
1957   - hash = ioctl32_hash (trans->cmd);
1958   - if (!ioctl32_hash_table[hash])
1959   - ioctl32_hash_table[hash] = trans;
1960   - else {
1961   - t = ioctl32_hash_table[hash];
1962   - while (t->next)
1963   - t = t->next;
1964   - trans->next = NULL;
1965   - t->next = trans;
1966   - }
  1965 + unsigned int a, b;
  1966 + a = *(unsigned int *)p;
  1967 + b = *(unsigned int *)q;
  1968 + if (a > b)
  1969 + return 1;
  1970 + if (a < b)
  1971 + return -1;
  1972 + return 0;
1967 1973 }
1968 1974  
1969 1975 static int __init init_sys32_ioctl(void)
1970 1976 {
1971   - int i;
1972   -
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   - }
  1977 + sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer),
  1978 + init_sys32_ioctl_cmp, NULL);
1981 1979 return 0;
1982 1980 }
1983 1981 __initcall(init_sys32_ioctl);