Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: x_tables: improve limit_mt scalability

We've seen this spin_lock show up high in profiles. Let's introduce a
lockless version. I've tested this using pktgen_sample01_simple.sh.

Signed-off-by: Jason Baron <jbaron@akamai.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Jason Baron and committed by
Pablo Neira Ayuso
07df3fc9 06f02993

+23 -17
+23 -17
net/netfilter/xt_limit.c
··· 8 8 #include <linux/slab.h> 9 9 #include <linux/module.h> 10 10 #include <linux/skbuff.h> 11 - #include <linux/spinlock.h> 12 11 #include <linux/interrupt.h> 13 12 14 13 #include <linux/netfilter/x_tables.h> 15 14 #include <linux/netfilter/xt_limit.h> 16 15 17 16 struct xt_limit_priv { 18 - spinlock_t lock; 19 17 unsigned long prev; 20 - uint32_t credit; 18 + u32 credit; 21 19 }; 22 20 23 21 MODULE_LICENSE("GPL"); ··· 64 66 { 65 67 const struct xt_rateinfo *r = par->matchinfo; 66 68 struct xt_limit_priv *priv = r->master; 67 - unsigned long now = jiffies; 69 + unsigned long now; 70 + u32 old_credit, new_credit, credit_increase = 0; 71 + bool ret; 68 72 69 - spin_lock_bh(&priv->lock); 70 - priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY; 71 - if (priv->credit > r->credit_cap) 72 - priv->credit = r->credit_cap; 73 + /* fastpath if there is nothing to update */ 74 + if ((READ_ONCE(priv->credit) < r->cost) && (READ_ONCE(priv->prev) == jiffies)) 75 + return false; 73 76 74 - if (priv->credit >= r->cost) { 75 - /* We're not limited. */ 76 - priv->credit -= r->cost; 77 - spin_unlock_bh(&priv->lock); 78 - return true; 79 - } 77 + do { 78 + now = jiffies; 79 + credit_increase += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY; 80 + old_credit = READ_ONCE(priv->credit); 81 + new_credit = old_credit; 82 + new_credit += credit_increase; 83 + if (new_credit > r->credit_cap) 84 + new_credit = r->credit_cap; 85 + if (new_credit >= r->cost) { 86 + ret = true; 87 + new_credit -= r->cost; 88 + } else { 89 + ret = false; 90 + } 91 + } while (cmpxchg(&priv->credit, old_credit, new_credit) != old_credit); 80 92 81 - spin_unlock_bh(&priv->lock); 82 - return false; 93 + return ret; 83 94 } 84 95 85 96 /* Precision saver. */ ··· 129 122 r->credit_cap = priv->credit; /* Credits full. */ 130 123 r->cost = user2credits(r->avg); 131 124 } 132 - spin_lock_init(&priv->lock); 133 125 134 126 return 0; 135 127 }