From patchwork Wed Dec 13 15:10:29 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Pirko X-Patchwork-Id: 848013 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="07vIn9mE"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yxgBk1xrRz9s71 for ; Thu, 14 Dec 2017 02:11:26 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753258AbdLMPLY (ORCPT ); Wed, 13 Dec 2017 10:11:24 -0500 Received: from mail-wr0-f196.google.com ([209.85.128.196]:43234 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751026AbdLMPKm (ORCPT ); Wed, 13 Dec 2017 10:10:42 -0500 Received: by mail-wr0-f196.google.com with SMTP id z34so2382457wrz.10 for ; Wed, 13 Dec 2017 07:10:42 -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=Tr+/WMxEloTgzd9ZC+840ZcRsgtbCtShIgWHBOUQKAI=; b=07vIn9mEkjWyi7h4UM2BDdbKLSvE6ktzp4q9exiyDN267AM5dNodGq8ZJCmT45ofwi 5Pkk0B5VTei1weHkx+6sUsmt1oe4Hnvxy2K0Je7Ipf9H8XP5ZHevQzRpFoz8U5SA9Tjb AvEir9bys3Pin3hhe4GF/eSUUKe6t/ggBv0pDEKayXKctZkfk+Jc5spZoKwZy5uG8l9D mMoOfB/sJ6y4MdiK6XudaDsd+e2embVF+AVfx68tUKrpWhMbJWydDiJiePo4MPOyYy+j TqkrMbBYJAUx9aIrFbyydftcXrRJQSFmCXx/ZZR1v0j20TWmuCwoPg4b3bDumW9rUcSG JaAw== 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=Tr+/WMxEloTgzd9ZC+840ZcRsgtbCtShIgWHBOUQKAI=; b=C9KsbACZIw1tvZ8fPox9kf9OIOBbZP6kdZLX4XOebk/q62UwfXYjQh2jmNtWrS3Eoj Ct0azZLdlrfXHsYHZ1/YML5ke7QLTVZ2F/dz25BjyvGEkU2ELak/s40QQsuDsjbVphPx xNNw0QxGbzCkuveaAC4vP14fUEY/eBtUHYAZMmko6kXBrZ2TVvUmB9nerWHiDCEOT91D twlfiZ9ktcx6hxUmpjbCX2EjqNJMQZwf501vmfCpZkMyb2PfgmHgwJ+fGKbVwKyixNoA c4tDFrLq7h7XAYryx3xPvW7/TE5tGiGKavFicYC65evgW2sPkE5+TFoqTH5UXpu0bmS5 2nvA== X-Gm-Message-State: AKGB3mKobNLECG1Mpb79rZVJfvGHqKV2g0oJpekGtryGU0A9wvg9ib4G NIWUgYkioTE+cEw7bCDCo1pdXTCu X-Google-Smtp-Source: ACJfBotN+FdFdmmvXPFtK5TCvb8BRQR+41P6cTb7r9sChTbW5T7oPFxZapa4pocvEP4/MzA3PELLtA== X-Received: by 10.223.151.34 with SMTP id r31mr2747775wrb.164.1513177841272; Wed, 13 Dec 2017 07:10:41 -0800 (PST) Received: from localhost (f190.dkm.cz. [62.24.70.190]) by smtp.gmail.com with ESMTPSA id a126sm954267wma.11.2017.12.13.07.10.40 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 13 Dec 2017 07:10:40 -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 Subject: [patch net-next v3 01/10] net: sched: introduce support for multiple filter chain pointers registration Date: Wed, 13 Dec 2017 16:10:29 +0100 Message-Id: <20171213151038.29665-2-jiri@resnulli.us> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171213151038.29665-1-jiri@resnulli.us> References: <20171213151038.29665-1-jiri@resnulli.us> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Jiri Pirko So far, there was possible only to register a single filter chain pointer to block->chain[0]. However, when the blocks will get shareable, we need to allow multiple filter chain pointers registration. Signed-off-by: Jiri Pirko --- v2->v3: - rebased on top of the current net-next --- include/net/pkt_cls.h | 3 + include/net/sch_generic.h | 5 +- net/sched/cls_api.c | 225 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 206 insertions(+), 27 deletions(-) diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 0105445..c04154f 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -29,6 +29,8 @@ 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; + bool shareable; + u32 block_index; }; struct tcf_block_cb; @@ -48,6 +50,7 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, static inline struct Qdisc *tcf_block_q(struct tcf_block *block) { + WARN_ON(block->refcnt != 1); return block->q; } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 8f8c0af..9ad1a51 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -269,8 +269,7 @@ typedef void tcf_chain_head_change_t(struct tcf_proto *tp_head, void *priv); struct tcf_chain { struct tcf_proto __rcu *filter_chain; - tcf_chain_head_change_t *chain_head_change; - void *chain_head_change_priv; + struct list_head filter_chain_list; struct list_head list; struct tcf_block *block; u32 index; /* chain index */ @@ -279,6 +278,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 5b9b8a6..dce4c3d 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -180,6 +181,12 @@ static void tcf_proto_destroy(struct tcf_proto *tp) kfree_rcu(tp, rcu); } +struct tcf_filter_chain_list_item { + struct list_head list; + tcf_chain_head_change_t *chain_head_change; + void *chain_head_change_priv; +}; + static struct tcf_chain *tcf_chain_create(struct tcf_block *block, u32 chain_index) { @@ -188,6 +195,7 @@ static struct tcf_chain *tcf_chain_create(struct tcf_block *block, chain = kzalloc(sizeof(*chain), GFP_KERNEL); if (!chain) return NULL; + INIT_LIST_HEAD(&chain->filter_chain_list); list_add_tail(&chain->list, &block->chain_list); chain->block = block; chain->index = chain_index; @@ -195,12 +203,19 @@ static struct tcf_chain *tcf_chain_create(struct tcf_block *block, return chain; } +static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, + struct tcf_proto *tp_head) +{ + if (item->chain_head_change) + item->chain_head_change(tp_head, item->chain_head_change_priv); +} static void tcf_chain_head_change(struct tcf_chain *chain, struct tcf_proto *tp_head) { - if (chain->chain_head_change) - chain->chain_head_change(tp_head, - chain->chain_head_change_priv); + struct tcf_filter_chain_list_item *item; + + list_for_each_entry(item, &chain->filter_chain_list, list) + tcf_chain_head_change_item(item, tp_head); } static void tcf_chain_flush(struct tcf_chain *chain) @@ -281,15 +296,84 @@ static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND); } -int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, - struct tcf_block_ext_info *ei) +static int +tcf_chain_head_change_cb_add(struct tcf_chain *chain, + struct tcf_block_ext_info *ei) +{ + struct tcf_filter_chain_list_item *item; + + item = kmalloc(sizeof(*item), GFP_KERNEL); + if (!item) + return -ENOMEM; + item->chain_head_change = ei->chain_head_change; + item->chain_head_change_priv = ei->chain_head_change_priv; + if (chain->filter_chain) + tcf_chain_head_change_item(item, chain->filter_chain); + list_add(&item->list, &chain->filter_chain_list); + return 0; +} + +static void +tcf_chain_head_change_cb_del(struct tcf_chain *chain, + struct tcf_block_ext_info *ei) +{ + struct tcf_filter_chain_list_item *item; + + list_for_each_entry(item, &chain->filter_chain_list, list) { + if ((!ei->chain_head_change && !ei->chain_head_change_priv) || + (item->chain_head_change == ei->chain_head_change && + item->chain_head_change_priv == ei->chain_head_change_priv)) { + tcf_chain_head_change_item(item, NULL); + list_del(&item->list); + kfree(item); + return; + } + } + WARN_ON(1); +} + +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 tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); + struct tcf_net *tn = net_generic(net, tcf_net_id); + int idr_start; + int idr_end; + int index; + + if (block_index >= INT_MAX) + return -EINVAL; + idr_start = block_index ? block_index : 1; + idr_end = block_index ? block_index + 1 : INT_MAX; + + index = idr_alloc(&tn->idr, block, idr_start, idr_end, GFP_KERNEL); + if (index < 0) + return index; + block->index = index; + return 0; +} + +static void tcf_block_remove(struct tcf_block *block, struct net *net) +{ + struct tcf_net *tn = net_generic(net, tcf_net_id); + + idr_remove(&tn->idr, block->index); +} + +static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q) +{ + struct tcf_block *block; struct tcf_chain *chain; int err; + block = kzalloc(sizeof(*block), GFP_KERNEL); if (!block) - return -ENOMEM; + return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&block->chain_list); INIT_LIST_HEAD(&block->cb_list); @@ -299,17 +383,72 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, err = -ENOMEM; goto err_chain_create; } - WARN_ON(!ei->chain_head_change); - chain->chain_head_change = ei->chain_head_change; - chain->chain_head_change_priv = ei->chain_head_change_priv; 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(&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 net *net = qdisc_net(q); + struct tcf_block *block = NULL; + bool created = false; + int err; + + if (ei->shareable) { + block = tcf_block_lookup(net, ei->block_index); + if (block) + block->refcnt++; + } + + if (!block) { + block = tcf_block_create(net, q); + if (IS_ERR(block)) + return PTR_ERR(block); + created = true; + if (ei->shareable) { + err = tcf_block_insert(block, net, ei->block_index); + if (err) + goto err_block_insert; + } + } + + err = tcf_chain_head_change_cb_add(tcf_block_chain_zero(block), ei); + if (err) + goto err_chain_head_change_cb_add; + tcf_block_offload_bind(block, q, ei); *p_block = block; return 0; -err_chain_create: - kfree(block); +err_chain_head_change_cb_add: + if (created) { + if (ei->shareable) + tcf_block_remove(block, net); +err_block_insert: + kfree(block); + } else { + block->refcnt--; + } return err; } EXPORT_SYMBOL(tcf_block_get_ext); @@ -342,24 +481,32 @@ 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. - */ - list_for_each_entry(chain, &block->chain_list, list) - tcf_chain_hold(chain); + tcf_chain_head_change_cb_del(tcf_block_chain_zero(block), ei); - list_for_each_entry(chain, &block->chain_list, list) - tcf_chain_flush(chain); + if (--block->refcnt == 0) { + if (ei->shareable) + tcf_block_remove(block, block->net); + + /* Hold a refcnt for all chains, except 0, 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. */ - chain = list_first_entry(&block->chain_list, struct tcf_chain, list); - tcf_chain_put(chain); + /* Finally, put chain 0 and allow block to be freed. */ + tcf_chain_put(tcf_block_chain_zero(block)); + } } EXPORT_SYMBOL(tcf_block_put_ext); @@ -1246,12 +1393,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,