From patchwork Fri Jan 12 15:46:52 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Pirko X-Patchwork-Id: 860021 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=resnulli-us.20150623.gappssmtp.com header.i=@resnulli-us.20150623.gappssmtp.com header.b="JWhIbjPt"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zJ6bq4rXjz9t3B for ; Sat, 13 Jan 2018 02:48:39 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934163AbeALPsb (ORCPT ); Fri, 12 Jan 2018 10:48:31 -0500 Received: from mail-wm0-f66.google.com ([74.125.82.66]:34448 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934007AbeALPrJ (ORCPT ); Fri, 12 Jan 2018 10:47:09 -0500 Received: by mail-wm0-f66.google.com with SMTP id 81so2946657wmb.1 for ; Fri, 12 Jan 2018 07:47:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=resnulli-us.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=uuOAXVbCsY9st2cRYlu/EAvRMgCBwI6BnUdYnzDF88Y=; b=JWhIbjPt0UI42TBsjQgf8cJP38/UYT6Njg844ZgJ8L/imXd8o0s+3IY0hqtR8FvxYB wTQjNREY44o4GwGH41UdegpBoUnMoUdyE7MJ9LURJEvy5UeZOoXuBEIE5GkgKjqupk4s +auojvs2IZYMfF3dr1SbeumDWVlzO3zT0Q36z8mbJeoNJH0TwoXLdeDdOqknX8d6SMy/ k2RiITjJftUTusHJKL60lB9k+TuMCsvkcWr5I4zPrcvckFiTJ/3CHvVcgg/fY4tWubtX cELvd1Sv1ioiAS8p3nejhQzmX5oQiq+Gwgant8TKuceK+TVaYjZTB5nLRZ+Ic80x6sB2 BZPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=uuOAXVbCsY9st2cRYlu/EAvRMgCBwI6BnUdYnzDF88Y=; b=o2UVo8HUQ7KeFSe7MezMh3bCFA7d0FpM9t7dizgdvXxxd555HrYBQIVBEuz9pGHdUz nRtfvL2zeNv8zH2KaUVOxPYeFxSxvLBXjTbfs9KLorhF3CdVE6ag4UdxGMtPhu+kXtjM QBNyYAnnkqziXuUSTK8jmLN9bAIl90TBr5+4E6zLro3nTDGem8a9pzQgdcrnkBhn/78O efM1HwsemalGbygHtNW3KQvFksNoWvfdLIvf3pL+RBZ6jlosdBxji4Af5WoKeVJ7iz/i QPFaTBqi6L0fQ1r3/ZgLWlVCX2ivjvY2wyZVtU/iLpG1ArmJweW51aX+m95QJ+8bKcCS gu6A== X-Gm-Message-State: AKwxyteFBVvV5X4B3gAzOuqc6nQ4D2+Bs0IRB6XzhQ+7Pesr33Mo/moL zIvfIUKcyRi6uBXyDHlRY+gCB/6j X-Google-Smtp-Source: ACJfBou2CwP6lH0IJC5+Bp2vcnnjFMsnTsu/R+SjBucox1jTmSxyDPsfnRSfAsK9ilSeLS8OQN/e5A== X-Received: by 10.28.40.195 with SMTP id o186mr4994512wmo.136.1515772027636; Fri, 12 Jan 2018 07:47:07 -0800 (PST) Received: from localhost (jirka.pirko.cz. [84.16.102.26]) by smtp.gmail.com with ESMTPSA id l9sm728735wrb.91.2018.01.12.07.47.07 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 12 Jan 2018 07:47:07 -0800 (PST) From: Jiri Pirko To: netdev@vger.kernel.org Cc: davem@davemloft.net, jhs@mojatatu.com, xiyou.wangcong@gmail.com, mlxsw@mellanox.com, andrew@lunn.ch, vivien.didelot@savoirfairelinux.com, f.fainelli@gmail.com, michael.chan@broadcom.com, ganeshgr@chelsio.com, saeedm@mellanox.com, matanb@mellanox.com, leonro@mellanox.com, idosch@mellanox.com, jakub.kicinski@netronome.com, simon.horman@netronome.com, pieter.jansenvanvuuren@netronome.com, john.hurley@netronome.com, alexander.h.duyck@intel.com, ogerlitz@mellanox.com, john.fastabend@gmail.com, daniel@iogearbox.net, dsahern@gmail.com Subject: [patch net-next v8 02/14] net: sched: introduce shared filter blocks infrastructure Date: Fri, 12 Jan 2018 16:46:52 +0100 Message-Id: <20180112154704.1694-3-jiri@resnulli.us> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20180112154704.1694-1-jiri@resnulli.us> References: <20180112154704.1694-1-jiri@resnulli.us> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Jiri Pirko Allow qdiscs to share filter blocks among them. Each qdisc type has to use block get/put extended modifications that enable sharing. Shared blocks are tracked within each net namespace and identified by u32 index. This index is passed from user during the qdisc creation. If user passes index that is not used by any other qdisc, new block is created. If user passes index that is already used, the existing block will be re-used. Signed-off-by: Jiri Pirko --- v6->v7: - new patch - splitted from the previous one as it got accidentaly squashed in the rebasing process in the past - converted to idr extended - removed auto-generating of block indexes. Callers have to explicily tell that the block is shared by passing non-zero block index - fixed error path in block get ext - freeing chain 0 --- include/net/pkt_cls.h | 7 ++ include/net/sch_generic.h | 2 + net/sched/cls_api.c | 163 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 148 insertions(+), 24 deletions(-) diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 0d1343c..f6a03ae 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -29,6 +29,7 @@ struct tcf_block_ext_info { enum tcf_block_binder_type binder_type; tcf_chain_head_change_t *chain_head_change; void *chain_head_change_priv; + u32 block_index; }; struct tcf_block_cb; @@ -48,8 +49,14 @@ void tcf_block_put(struct tcf_block *block); void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, struct tcf_block_ext_info *ei); +static inline bool tcf_block_shared(struct tcf_block *block) +{ + return block->index; +} + static inline struct Qdisc *tcf_block_q(struct tcf_block *block) { + WARN_ON(tcf_block_shared(block)); return block->q; } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index ddb96bb..5cc4d71 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -284,6 +284,8 @@ struct tcf_chain { struct tcf_block { struct list_head chain_list; + u32 index; /* block index for shared blocks */ + unsigned int refcnt; struct net *net; struct Qdisc *q; struct list_head cb_list; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index e6b16b3..9b45950 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -333,22 +334,44 @@ tcf_chain_head_change_cb_del(struct tcf_chain *chain, WARN_ON(1); } -static struct tcf_chain *tcf_block_chain_zero(struct tcf_block *block) +struct tcf_net { + struct idr idr; +}; + +static unsigned int tcf_net_id; + +static int tcf_block_insert(struct tcf_block *block, struct net *net, + u32 block_index, struct netlink_ext_ack *extack) { - return list_first_entry(&block->chain_list, struct tcf_chain, list); + struct tcf_net *tn = net_generic(net, tcf_net_id); + int err; + + err = idr_alloc_ext(&tn->idr, block, NULL, block_index, + block_index + 1, GFP_KERNEL); + if (err) + return err; + block->index = block_index; + return 0; } -int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, - struct tcf_block_ext_info *ei, - struct netlink_ext_ack *extack) +static void tcf_block_remove(struct tcf_block *block, struct net *net) +{ + struct tcf_net *tn = net_generic(net, tcf_net_id); + + idr_remove_ext(&tn->idr, block->index); +} + +static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q, + struct netlink_ext_ack *extack) { - struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); + struct tcf_block *block; struct tcf_chain *chain; int err; + block = kzalloc(sizeof(*block), GFP_KERNEL); if (!block) { NL_SET_ERR_MSG(extack, "Memory allocation for block failed"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } INIT_LIST_HEAD(&block->chain_list); INIT_LIST_HEAD(&block->cb_list); @@ -360,20 +383,76 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, err = -ENOMEM; goto err_chain_create; } + block->net = qdisc_net(q); + block->refcnt = 1; + block->net = net; + block->q = q; + return block; + +err_chain_create: + kfree(block); + return ERR_PTR(err); +} + +static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index) +{ + struct tcf_net *tn = net_generic(net, tcf_net_id); + + return idr_find_ext(&tn->idr, block_index); +} + +static struct tcf_chain *tcf_block_chain_zero(struct tcf_block *block) +{ + return list_first_entry(&block->chain_list, struct tcf_chain, list); +} + +int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, + struct tcf_block_ext_info *ei, + struct netlink_ext_ack *extack) +{ + struct net *net = qdisc_net(q); + struct tcf_block *block = NULL; + bool created = false; + int err; + + if (ei->block_index) { + /* block_index not 0 means the shared block is requested */ + block = tcf_block_lookup(net, ei->block_index); + if (block) + block->refcnt++; + } + + if (!block) { + block = tcf_block_create(net, q, extack); + if (IS_ERR(block)) + return PTR_ERR(block); + created = true; + if (ei->block_index) { + err = tcf_block_insert(block, net, + ei->block_index, extack); + if (err) + goto err_block_insert; + } + } + err = tcf_chain_head_change_cb_add(tcf_block_chain_zero(block), ei, extack); if (err) goto err_chain_head_change_cb_add; - block->net = qdisc_net(q); - block->q = q; tcf_block_offload_bind(block, q, ei); *p_block = block; return 0; -err_chain_create: - kfree(block); err_chain_head_change_cb_add: - kfree(chain); + if (created) { + if (tcf_block_shared(block)) + tcf_block_remove(block, net); +err_block_insert: + kfree(tcf_block_chain_zero(block)); + kfree(block); + } else { + block->refcnt--; + } return err; } EXPORT_SYMBOL(tcf_block_get_ext); @@ -407,26 +486,34 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, { struct tcf_chain *chain, *tmp; - /* Hold a refcnt for all chains, so that they don't disappear - * while we are iterating. - */ if (!block) return; tcf_chain_head_change_cb_del(tcf_block_chain_zero(block), ei); - list_for_each_entry(chain, &block->chain_list, list) - tcf_chain_hold(chain); - list_for_each_entry(chain, &block->chain_list, list) - tcf_chain_flush(chain); + if (--block->refcnt == 0) { + if (tcf_block_shared(block)) + tcf_block_remove(block, block->net); + + /* Hold a refcnt for all chains, so that they don't disappear + * while we are iterating. + */ + list_for_each_entry(chain, &block->chain_list, list) + tcf_chain_hold(chain); + + list_for_each_entry(chain, &block->chain_list, list) + tcf_chain_flush(chain); + } tcf_block_offload_unbind(block, q, ei); - /* At this point, all the chains should have refcnt >= 1. */ - list_for_each_entry_safe(chain, tmp, &block->chain_list, list) - tcf_chain_put(chain); + if (block->refcnt == 0) { + /* At this point, all the chains should have refcnt >= 1. */ + list_for_each_entry_safe(chain, tmp, &block->chain_list, list) + tcf_chain_put(chain); - /* Finally, put chain 0 and allow block to be freed. */ - tcf_chain_put(tcf_block_chain_zero(block)); + /* Finally, put chain 0 and allow block to be freed. */ + tcf_chain_put(tcf_block_chain_zero(block)); + } } EXPORT_SYMBOL(tcf_block_put_ext); @@ -1313,12 +1400,40 @@ int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts, } EXPORT_SYMBOL(tc_setup_cb_call); +static __net_init int tcf_net_init(struct net *net) +{ + struct tcf_net *tn = net_generic(net, tcf_net_id); + + idr_init(&tn->idr); + return 0; +} + +static void __net_exit tcf_net_exit(struct net *net) +{ + struct tcf_net *tn = net_generic(net, tcf_net_id); + + idr_destroy(&tn->idr); +} + +static struct pernet_operations tcf_net_ops = { + .init = tcf_net_init, + .exit = tcf_net_exit, + .id = &tcf_net_id, + .size = sizeof(struct tcf_net), +}; + static int __init tc_filter_init(void) { + int err; + tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); if (!tc_filter_wq) return -ENOMEM; + err = register_pernet_subsys(&tcf_net_ops); + if (err) + return err; + rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, 0); rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,