Skip to content

Commit

Permalink
netfilter: arp_tables: unfold two critical loops in arp_packet_match()
Browse files Browse the repository at this point in the history
x86 and powerpc can perform long word accesses in an efficient maner.
We can use this to unroll two loops in arp_packet_match(), to
perform arithmetic on long words instead of bytes. This is a win
on x86_64 for example.

Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
  • Loading branch information
Eric Dumazet authored and Patrick McHardy committed Feb 18, 2009
1 parent 55df4ac commit ddc214c
Showing 1 changed file with 34 additions and 10 deletions.
44 changes: 34 additions & 10 deletions net/ipv4/netfilter/arp_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,36 @@ static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap,
return (ret != 0);
}

/*
* Unfortunatly, _b and _mask are not aligned to an int (or long int)
* Some arches dont care, unrolling the loop is a win on them.
*/
static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
{
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
const unsigned long *a = (const unsigned long *)_a;
const unsigned long *b = (const unsigned long *)_b;
const unsigned long *mask = (const unsigned long *)_mask;
unsigned long ret;

ret = (a[0] ^ b[0]) & mask[0];
if (IFNAMSIZ > sizeof(unsigned long))
ret |= (a[1] ^ b[1]) & mask[1];
if (IFNAMSIZ > 2 * sizeof(unsigned long))
ret |= (a[2] ^ b[2]) & mask[2];
if (IFNAMSIZ > 3 * sizeof(unsigned long))
ret |= (a[3] ^ b[3]) & mask[3];
BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
#else
unsigned long ret = 0;
int i;

for (i = 0; i < IFNAMSIZ; i++)
ret |= (_a[i] ^ _b[i]) & _mask[i];
#endif
return ret;
}

/* Returns whether packet matches rule or not. */
static inline int arp_packet_match(const struct arphdr *arphdr,
struct net_device *dev,
Expand All @@ -83,7 +113,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
const char *arpptr = (char *)(arphdr + 1);
const char *src_devaddr, *tgt_devaddr;
__be32 src_ipaddr, tgt_ipaddr;
int i, ret;
long ret;

#define FWINV(bool, invflg) ((bool) ^ !!(arpinfo->invflags & (invflg)))

Expand Down Expand Up @@ -156,10 +186,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
}

/* Look for ifname matches. */
for (i = 0, ret = 0; i < IFNAMSIZ; i++) {
ret |= (indev[i] ^ arpinfo->iniface[i])
& arpinfo->iniface_mask[i];
}
ret = ifname_compare(indev, arpinfo->iniface, arpinfo->iniface_mask);

if (FWINV(ret != 0, ARPT_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n",
Expand All @@ -168,10 +195,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
return 0;
}

for (i = 0, ret = 0; i < IFNAMSIZ; i++) {
ret |= (outdev[i] ^ arpinfo->outiface[i])
& arpinfo->outiface_mask[i];
}
ret = ifname_compare(outdev, arpinfo->outiface, arpinfo->outiface_mask);

if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n",
Expand Down Expand Up @@ -221,7 +245,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
const struct net_device *out,
struct xt_table *table)
{
static const char nulldevname[IFNAMSIZ];
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
unsigned int verdict = NF_DROP;
const struct arphdr *arp;
bool hotdrop = false;
Expand Down

0 comments on commit ddc214c

Please sign in to comment.