From patchwork Wed Aug 19 19:18:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 508829 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 5ACB414076C for ; Thu, 20 Aug 2015 05:13:52 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752000AbbHSTNn (ORCPT ); Wed, 19 Aug 2015 15:13:43 -0400 Received: from mail.us.es ([193.147.175.20]:44910 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751743AbbHSTMo (ORCPT ); Wed, 19 Aug 2015 15:12:44 -0400 Received: (qmail 9947 invoked from network); 19 Aug 2015 21:12:42 +0200 Received: from unknown (HELO us.es) (192.168.2.15) by us.es with SMTP; 19 Aug 2015 21:12:42 +0200 Received: (qmail 479 invoked by uid 507); 19 Aug 2015 19:12:42 -0000 X-Qmail-Scanner-Diagnostics: from 127.0.0.1 by antivirus5 (envelope-from , uid 501) with qmail-scanner-2.10 (clamdscan: 0.98.7/20809. spamassassin: 3.4.0. Clear:RC:1(127.0.0.1):SA:0(-102.8/7.5):. Processed in 4.874127 secs); 19 Aug 2015 19:12:42 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on antivirus5 X-Spam-Level: X-Spam-Status: No, score=-102.8 required=7.5 tests=BAYES_50,RDNS_NONE, SMTPAUTH_US,USER_IN_WHITELIST autolearn=disabled version=3.4.0 X-Spam-ASN: AS18647 69.84.240.0/20 X-Envelope-From: pablo@netfilter.org Received: from unknown (HELO antivirus5) (127.0.0.1) by us.es with SMTP; 19 Aug 2015 19:12:37 -0000 Received: from 192.168.1.13 (192.168.1.13) by antivirus5 (F-Secure/fsigk_smtp/412/antivirus5); Wed, 19 Aug 2015 21:12:37 +0200 (CEST) X-Virus-Status: clean(F-Secure/fsigk_smtp/412/antivirus5) Received: (qmail 17228 invoked from network); 19 Aug 2015 21:12:37 +0200 Received: from unknown (HELO salvia.event.rightround.com) (pneira@us.es@69.84.245.29) by mail.us.es with SMTP; 19 Aug 2015 21:12:37 +0200 From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Cc: davem@davemloft.net, netdev@vger.kernel.org Subject: [PATCH 06/15] netfilter: nft_limit: convert to token-based limiting at nanosecond granularity Date: Wed, 19 Aug 2015 21:18:22 +0200 Message-Id: <1440011911-4359-7-git-send-email-pablo@netfilter.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1440011911-4359-1-git-send-email-pablo@netfilter.org> References: <1440011911-4359-1-git-send-email-pablo@netfilter.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Rework the limit expression to use a token-based limiting approach that refills the bucket gradually. The tokens are calculated at nanosecond granularity instead jiffies to improve precision. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_limit.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index d0788e1..c79703e 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -20,10 +20,11 @@ static DEFINE_SPINLOCK(limit_lock); struct nft_limit { + u64 last; u64 tokens; + u64 tokens_max; u64 rate; - u64 unit; - unsigned long stamp; + u64 nsecs; }; static void nft_limit_pkts_eval(const struct nft_expr *expr, @@ -31,18 +32,23 @@ static void nft_limit_pkts_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_limit *priv = nft_expr_priv(expr); + u64 now, tokens, cost = div_u64(priv->nsecs, priv->rate); + s64 delta; spin_lock_bh(&limit_lock); - if (time_after_eq(jiffies, priv->stamp)) { - priv->tokens = priv->rate; - priv->stamp = jiffies + priv->unit * HZ; - } - - if (priv->tokens >= 1) { - priv->tokens--; + now = ktime_get_ns(); + tokens = priv->tokens + now - priv->last; + if (tokens > priv->tokens_max) + tokens = priv->tokens_max; + + priv->last = now; + delta = tokens - cost; + if (delta >= 0) { + priv->tokens = delta; spin_unlock_bh(&limit_lock); return; } + priv->tokens = tokens; spin_unlock_bh(&limit_lock); regs->verdict.code = NFT_BREAK; @@ -58,25 +64,29 @@ static int nft_limit_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_limit *priv = nft_expr_priv(expr); + u64 unit; if (tb[NFTA_LIMIT_RATE] == NULL || tb[NFTA_LIMIT_UNIT] == NULL) return -EINVAL; - priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); - priv->unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); - priv->stamp = jiffies + priv->unit * HZ; - priv->tokens = priv->rate; + priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); + unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); + priv->nsecs = unit * NSEC_PER_SEC; + if (priv->rate == 0 || priv->nsecs < unit) + return -EOVERFLOW; + priv->tokens = priv->tokens_max = priv->nsecs; + priv->last = ktime_get_ns(); return 0; } static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr) { const struct nft_limit *priv = nft_expr_priv(expr); + u64 secs = div_u64(priv->nsecs, NSEC_PER_SEC); - if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate))) - goto nla_put_failure; - if (nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(priv->unit))) + if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate)) || + nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs))) goto nla_put_failure; return 0;