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 @@ -1236,15 +1236,33 @@ jump_fast(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; + bool used_cache; /* 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 { + used_cache = false; + if (num != IP_FW_TARG) { +#ifdef __LP64__ + cache.k = f->cache.k; + if (cache.id == chain->id) { + used_cache = true; + f_pos = cache.pos; + } +#else + if (f->cache.id == chain->id) { + used_cache = true; + /* Load pos after id */ + atomic_thread_fence_acq(); + f_pos = f->cache.pos; + } +#endif + } + + if (!used_cache) { int i = IP_FW_ARG_TABLEARG(chain, num, skipto); /* make sure we do not jump backward */ if (jump_backwards == 0 && i <= f->rulenum) @@ -1255,8 +1273,21 @@ 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; + /* + * 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.k = cache.k; +#else + f->cache.pos = f_pos; + /* Store id after pos */ + atomic_thread_fence_rel(); + f->cache.id = chain->id; +#endif } } 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 k; + }; +}; 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 */