filter: Optimize instruction revalidation code.
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Tue, 16 Nov 2010 15:19:51 +0000 (15:19 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 18 Nov 2010 18:58:35 +0000 (10:58 -0800)
Since repeating u16 value to u8 value conversion using switch() clause's
case statement is wasteful, this patch introduces u16 to u8 mapping table
and removes most of case statements. As a result, the size of net/core/filter.o
is reduced by about 29% on x86.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/filter.c

index 23e9b2a6b4c8ade67fff929c78cea57483b49964..03dc0710194f12c5251d1e291c26a12247d5ea49 100644 (file)
@@ -383,7 +383,57 @@ EXPORT_SYMBOL(sk_run_filter);
  */
 int sk_chk_filter(struct sock_filter *filter, int flen)
 {
-       struct sock_filter *ftest;
+       /*
+        * Valid instructions are initialized to non-0.
+        * Invalid instructions are initialized to 0.
+        */
+       static const u8 codes[] = {
+               [BPF_ALU|BPF_ADD|BPF_K]  = BPF_S_ALU_ADD_K + 1,
+               [BPF_ALU|BPF_ADD|BPF_X]  = BPF_S_ALU_ADD_X + 1,
+               [BPF_ALU|BPF_SUB|BPF_K]  = BPF_S_ALU_SUB_K + 1,
+               [BPF_ALU|BPF_SUB|BPF_X]  = BPF_S_ALU_SUB_X + 1,
+               [BPF_ALU|BPF_MUL|BPF_K]  = BPF_S_ALU_MUL_K + 1,
+               [BPF_ALU|BPF_MUL|BPF_X]  = BPF_S_ALU_MUL_X + 1,
+               [BPF_ALU|BPF_DIV|BPF_X]  = BPF_S_ALU_DIV_X + 1,
+               [BPF_ALU|BPF_AND|BPF_K]  = BPF_S_ALU_AND_K + 1,
+               [BPF_ALU|BPF_AND|BPF_X]  = BPF_S_ALU_AND_X + 1,
+               [BPF_ALU|BPF_OR|BPF_K]   = BPF_S_ALU_OR_K + 1,
+               [BPF_ALU|BPF_OR|BPF_X]   = BPF_S_ALU_OR_X + 1,
+               [BPF_ALU|BPF_LSH|BPF_K]  = BPF_S_ALU_LSH_K + 1,
+               [BPF_ALU|BPF_LSH|BPF_X]  = BPF_S_ALU_LSH_X + 1,
+               [BPF_ALU|BPF_RSH|BPF_K]  = BPF_S_ALU_RSH_K + 1,
+               [BPF_ALU|BPF_RSH|BPF_X]  = BPF_S_ALU_RSH_X + 1,
+               [BPF_ALU|BPF_NEG]        = BPF_S_ALU_NEG + 1,
+               [BPF_LD|BPF_W|BPF_ABS]   = BPF_S_LD_W_ABS + 1,
+               [BPF_LD|BPF_H|BPF_ABS]   = BPF_S_LD_H_ABS + 1,
+               [BPF_LD|BPF_B|BPF_ABS]   = BPF_S_LD_B_ABS + 1,
+               [BPF_LD|BPF_W|BPF_LEN]   = BPF_S_LD_W_LEN + 1,
+               [BPF_LD|BPF_W|BPF_IND]   = BPF_S_LD_W_IND + 1,
+               [BPF_LD|BPF_H|BPF_IND]   = BPF_S_LD_H_IND + 1,
+               [BPF_LD|BPF_B|BPF_IND]   = BPF_S_LD_B_IND + 1,
+               [BPF_LD|BPF_IMM]         = BPF_S_LD_IMM + 1,
+               [BPF_LDX|BPF_W|BPF_LEN]  = BPF_S_LDX_W_LEN + 1,
+               [BPF_LDX|BPF_B|BPF_MSH]  = BPF_S_LDX_B_MSH + 1,
+               [BPF_LDX|BPF_IMM]        = BPF_S_LDX_IMM + 1,
+               [BPF_MISC|BPF_TAX]       = BPF_S_MISC_TAX + 1,
+               [BPF_MISC|BPF_TXA]       = BPF_S_MISC_TXA + 1,
+               [BPF_RET|BPF_K]          = BPF_S_RET_K + 1,
+               [BPF_RET|BPF_A]          = BPF_S_RET_A + 1,
+               [BPF_ALU|BPF_DIV|BPF_K]  = BPF_S_ALU_DIV_K + 1,
+               [BPF_LD|BPF_MEM]         = BPF_S_LD_MEM + 1,
+               [BPF_LDX|BPF_MEM]        = BPF_S_LDX_MEM + 1,
+               [BPF_ST]                 = BPF_S_ST + 1,
+               [BPF_STX]                = BPF_S_STX + 1,
+               [BPF_JMP|BPF_JA]         = BPF_S_JMP_JA + 1,
+               [BPF_JMP|BPF_JEQ|BPF_K]  = BPF_S_JMP_JEQ_K + 1,
+               [BPF_JMP|BPF_JEQ|BPF_X]  = BPF_S_JMP_JEQ_X + 1,
+               [BPF_JMP|BPF_JGE|BPF_K]  = BPF_S_JMP_JGE_K + 1,
+               [BPF_JMP|BPF_JGE|BPF_X]  = BPF_S_JMP_JGE_X + 1,
+               [BPF_JMP|BPF_JGT|BPF_K]  = BPF_S_JMP_JGT_K + 1,
+               [BPF_JMP|BPF_JGT|BPF_X]  = BPF_S_JMP_JGT_X + 1,
+               [BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K + 1,
+               [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X + 1,
+       };
        int pc;
 
        if (flen == 0 || flen > BPF_MAXINSNS)
@@ -391,136 +441,31 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
 
        /* check the filter code now */
        for (pc = 0; pc < flen; pc++) {
-               ftest = &filter[pc];
-
-               /* Only allow valid instructions */
-               switch (ftest->code) {
-               case BPF_ALU|BPF_ADD|BPF_K:
-                       ftest->code = BPF_S_ALU_ADD_K;
-                       break;
-               case BPF_ALU|BPF_ADD|BPF_X:
-                       ftest->code = BPF_S_ALU_ADD_X;
-                       break;
-               case BPF_ALU|BPF_SUB|BPF_K:
-                       ftest->code = BPF_S_ALU_SUB_K;
-                       break;
-               case BPF_ALU|BPF_SUB|BPF_X:
-                       ftest->code = BPF_S_ALU_SUB_X;
-                       break;
-               case BPF_ALU|BPF_MUL|BPF_K:
-                       ftest->code = BPF_S_ALU_MUL_K;
-                       break;
-               case BPF_ALU|BPF_MUL|BPF_X:
-                       ftest->code = BPF_S_ALU_MUL_X;
-                       break;
-               case BPF_ALU|BPF_DIV|BPF_X:
-                       ftest->code = BPF_S_ALU_DIV_X;
-                       break;
-               case BPF_ALU|BPF_AND|BPF_K:
-                       ftest->code = BPF_S_ALU_AND_K;
-                       break;
-               case BPF_ALU|BPF_AND|BPF_X:
-                       ftest->code = BPF_S_ALU_AND_X;
-                       break;
-               case BPF_ALU|BPF_OR|BPF_K:
-                       ftest->code = BPF_S_ALU_OR_K;
-                       break;
-               case BPF_ALU|BPF_OR|BPF_X:
-                       ftest->code = BPF_S_ALU_OR_X;
-                       break;
-               case BPF_ALU|BPF_LSH|BPF_K:
-                       ftest->code = BPF_S_ALU_LSH_K;
-                       break;
-               case BPF_ALU|BPF_LSH|BPF_X:
-                       ftest->code = BPF_S_ALU_LSH_X;
-                       break;
-               case BPF_ALU|BPF_RSH|BPF_K:
-                       ftest->code = BPF_S_ALU_RSH_K;
-                       break;
-               case BPF_ALU|BPF_RSH|BPF_X:
-                       ftest->code = BPF_S_ALU_RSH_X;
-                       break;
-               case BPF_ALU|BPF_NEG:
-                       ftest->code = BPF_S_ALU_NEG;
-                       break;
-               case BPF_LD|BPF_W|BPF_ABS:
-                       ftest->code = BPF_S_LD_W_ABS;
-                       break;
-               case BPF_LD|BPF_H|BPF_ABS:
-                       ftest->code = BPF_S_LD_H_ABS;
-                       break;
-               case BPF_LD|BPF_B|BPF_ABS:
-                       ftest->code = BPF_S_LD_B_ABS;
-                       break;
-               case BPF_LD|BPF_W|BPF_LEN:
-                       ftest->code = BPF_S_LD_W_LEN;
-                       break;
-               case BPF_LD|BPF_W|BPF_IND:
-                       ftest->code = BPF_S_LD_W_IND;
-                       break;
-               case BPF_LD|BPF_H|BPF_IND:
-                       ftest->code = BPF_S_LD_H_IND;
-                       break;
-               case BPF_LD|BPF_B|BPF_IND:
-                       ftest->code = BPF_S_LD_B_IND;
-                       break;
-               case BPF_LD|BPF_IMM:
-                       ftest->code = BPF_S_LD_IMM;
-                       break;
-               case BPF_LDX|BPF_W|BPF_LEN:
-                       ftest->code = BPF_S_LDX_W_LEN;
-                       break;
-               case BPF_LDX|BPF_B|BPF_MSH:
-                       ftest->code = BPF_S_LDX_B_MSH;
-                       break;
-               case BPF_LDX|BPF_IMM:
-                       ftest->code = BPF_S_LDX_IMM;
-                       break;
-               case BPF_MISC|BPF_TAX:
-                       ftest->code = BPF_S_MISC_TAX;
-                       break;
-               case BPF_MISC|BPF_TXA:
-                       ftest->code = BPF_S_MISC_TXA;
-                       break;
-               case BPF_RET|BPF_K:
-                       ftest->code = BPF_S_RET_K;
-                       break;
-               case BPF_RET|BPF_A:
-                       ftest->code = BPF_S_RET_A;
-                       break;
+               struct sock_filter *ftest = &filter[pc];
+               u16 code = ftest->code;
 
+               if (code >= ARRAY_SIZE(codes))
+                       return -EINVAL;
+               code = codes[code];
+               /* Undo the '+ 1' in codes[] after validation. */
+               if (!code--)
+                       return -EINVAL;
                /* Some instructions need special checks */
-
+               switch (code) {
+               case BPF_S_ALU_DIV_K:
                        /* check for division by zero */
-               case BPF_ALU|BPF_DIV|BPF_K:
                        if (ftest->k == 0)
                                return -EINVAL;
-                       ftest->code = BPF_S_ALU_DIV_K;
                        break;
-
-               /* check for invalid memory addresses */
-               case BPF_LD|BPF_MEM:
-                       if (ftest->k >= BPF_MEMWORDS)
-                               return -EINVAL;
-                       ftest->code = BPF_S_LD_MEM;
-                       break;
-               case BPF_LDX|BPF_MEM:
-                       if (ftest->k >= BPF_MEMWORDS)
-                               return -EINVAL;
-                       ftest->code = BPF_S_LDX_MEM;
-                       break;
-               case BPF_ST:
-                       if (ftest->k >= BPF_MEMWORDS)
-                               return -EINVAL;
-                       ftest->code = BPF_S_ST;
-                       break;
-               case BPF_STX:
+               case BPF_S_LD_MEM:
+               case BPF_S_LDX_MEM:
+               case BPF_S_ST:
+               case BPF_S_STX:
+                       /* check for invalid memory addresses */
                        if (ftest->k >= BPF_MEMWORDS)
                                return -EINVAL;
-                       ftest->code = BPF_S_STX;
                        break;
-
-               case BPF_JMP|BPF_JA:
+               case BPF_S_JMP_JA:
                        /*
                         * Note, the large ftest->k might cause loops.
                         * Compare this with conditional jumps below,
@@ -528,40 +473,7 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
                         */
                        if (ftest->k >= (unsigned)(flen-pc-1))
                                return -EINVAL;
-                       ftest->code = BPF_S_JMP_JA;
-                       break;
-
-               case BPF_JMP|BPF_JEQ|BPF_K:
-                       ftest->code = BPF_S_JMP_JEQ_K;
-                       break;
-               case BPF_JMP|BPF_JEQ|BPF_X:
-                       ftest->code = BPF_S_JMP_JEQ_X;
                        break;
-               case BPF_JMP|BPF_JGE|BPF_K:
-                       ftest->code = BPF_S_JMP_JGE_K;
-                       break;
-               case BPF_JMP|BPF_JGE|BPF_X:
-                       ftest->code = BPF_S_JMP_JGE_X;
-                       break;
-               case BPF_JMP|BPF_JGT|BPF_K:
-                       ftest->code = BPF_S_JMP_JGT_K;
-                       break;
-               case BPF_JMP|BPF_JGT|BPF_X:
-                       ftest->code = BPF_S_JMP_JGT_X;
-                       break;
-               case BPF_JMP|BPF_JSET|BPF_K:
-                       ftest->code = BPF_S_JMP_JSET_K;
-                       break;
-               case BPF_JMP|BPF_JSET|BPF_X:
-                       ftest->code = BPF_S_JMP_JSET_X;
-                       break;
-
-               default:
-                       return -EINVAL;
-               }
-
-                       /* for conditionals both must be safe */
-               switch (ftest->code) {
                case BPF_S_JMP_JEQ_K:
                case BPF_S_JMP_JEQ_X:
                case BPF_S_JMP_JGE_K:
@@ -570,10 +482,13 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
                case BPF_S_JMP_JGT_X:
                case BPF_S_JMP_JSET_X:
                case BPF_S_JMP_JSET_K:
+                       /* for conditionals both must be safe */
                        if (pc + ftest->jt + 1 >= flen ||
                            pc + ftest->jf + 1 >= flen)
                                return -EINVAL;
+                       break;
                }
+               ftest->code = code;
        }
 
        /* last instruction must be a RET code */
@@ -581,10 +496,8 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
        case BPF_S_RET_K:
        case BPF_S_RET_A:
                return 0;
-               break;
-               default:
-                       return -EINVAL;
-               }
+       }
+       return -EINVAL;
 }
 EXPORT_SYMBOL(sk_chk_filter);