diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -144,14 +144,14 @@ /* Use 128 tables by default */ static unsigned int default_fw_tables = IPFW_TABLES_DEFAULT; +static int jump_lookup_pos(struct ip_fw_chain *chain, struct ip_fw *f, int num, + int tablearg, int jump_backwards); #ifndef LINEAR_SKIPTO -static int jump_fast(struct ip_fw_chain *chain, struct ip_fw *f, int num, +static int jump_use_cache(struct ip_fw_chain *chain, struct ip_fw *f, int num, int tablearg, int jump_backwards); -#define JUMP(ch, f, num, targ, back) jump_fast(ch, f, num, targ, back) +#define JUMP(ch, f, num, targ, back) jump_use_cache(ch, f, num, targ, back) #else -static int jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num, - int tablearg, int jump_backwards); -#define JUMP(ch, f, num, targ, back) jump_linear(ch, f, num, targ, back) +#define JUMP(ch, f, num, targ, back) jump_lookup_pos(ch, f, num, targ, back) #endif /* @@ -1227,60 +1227,80 @@ args->flags |= IPFW_ARGS_REF; } +static int +jump_lookup_pos(struct ip_fw_chain *chain, struct ip_fw *f, int num, + int tablearg, int jump_backwards) +{ + int f_pos, i; + + i = IP_FW_ARG_TABLEARG(chain, num, skipto); + /* make sure we do not jump backward */ + if (jump_backwards == 0 && i <= f->rulenum) + i = f->rulenum + 1; + #ifndef LINEAR_SKIPTO + if (chain->idxmap != NULL) + f_pos = chain->idxmap[i]; + else + f_pos = ipfw_find_rule(chain, i, 0); +#else + f_pos = chain->idxmap[i]; +#endif + + return (f_pos); +} + /* * Helper function to enable cached rule lookups using * cached_id and cached_pos fields in ipfw rule. */ static int -jump_fast(struct ip_fw_chain *chain, struct ip_fw *f, int num, +jump_use_cache(struct ip_fw_chain *chain, struct ip_fw *f, int num, int tablearg, int jump_backwards) { + struct ip_fw_jump_cache cache; int f_pos; + /* Can't use cache with IP_FW_TARG */ + if (num == IP_FW_TARG) + return jump_lookup_pos(chain, f, num, tablearg, jump_backwards); + /* If possible use cached f_pos (in f->cached_pos), * whose version is written in f->cached_id * (horrible hacks to avoid changing the ABI). */ - if (num != IP_FW_TARG && f->cached_id == chain->id) - f_pos = f->cached_pos; - else { - int i = IP_FW_ARG_TABLEARG(chain, num, skipto); - /* make sure we do not jump backward */ - if (jump_backwards == 0 && i <= f->rulenum) - i = f->rulenum + 1; - if (chain->idxmap != NULL) - f_pos = chain->idxmap[i]; - else - f_pos = ipfw_find_rule(chain, i, 0); - /* update the cache */ - if (num != IP_FW_TARG) { - f->cached_id = chain->id; - f->cached_pos = f_pos; - } +#ifdef __LP64__ + cache.raw_value = f->cache.raw_value; + if (cache.id == chain->id) + return (cache.pos); +#else + if (f->cache.id == chain->id) { + /* Load pos after id */ + atomic_thread_fence_acq(); + return (f->cache.pos); } +#endif - return (f_pos); -} -#else -/* - * Helper function to enable real fast rule lookups. - */ -static int -jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num, - int tablearg, int jump_backwards) -{ - int f_pos; + f_pos = jump_lookup_pos(chain, f, num, tablearg, jump_backwards); - num = IP_FW_ARG_TABLEARG(chain, num, skipto); - /* make sure we do not jump backward */ - if (jump_backwards == 0 && num <= f->rulenum) - num = f->rulenum + 1; - f_pos = chain->idxmap[num]; + /* Update the cache + * We don't care about thread synchronization there. + * Even if multiple threads update the cache simultaneously, + * we still get the correct cache value. + */ +#ifdef __LP64__ + cache.pos = f_pos; + cache.id = chain->id; + f->cache.raw_value = cache.raw_value; +#else + f->cache.pos = f_pos; + /* Store id after pos */ + atomic_thread_fence_rel(); + f->cache.id = chain->id; +#endif return (f_pos); } -#endif #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) /* diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -265,6 +265,15 @@ * ACTION_PTR(r) is the start of the first action (things to do * once a rule matched). */ +struct ip_fw_jump_cache { + union { + struct { + uint32_t id; + uint32_t pos; + }; + uint64_t raw_value; + }; +}; struct ip_fw { uint16_t act_ofs; /* offset of action in 32-bit units */ @@ -273,10 +282,9 @@ uint8_t set; /* rule set (0..31) */ uint8_t flags; /* currently unused */ counter_u64_t cntr; /* Pointer to rule counters */ + struct ip_fw_jump_cache cache; /* used by jump_fast */ uint32_t timestamp; /* tv_sec of last match */ uint32_t id; /* rule id */ - uint32_t cached_id; /* used by jump_fast */ - uint32_t cached_pos; /* used by jump_fast */ uint32_t refcnt; /* number of references */ struct ip_fw *next; /* linked list of deleted rules */