From patchwork Sat May 20 00:58:23 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nambiar, Amritha" X-Patchwork-Id: 764840 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wV0485Mzyz9s2s for ; Sat, 20 May 2017 06:28:08 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 519BC8A171; Fri, 19 May 2017 20:28:07 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id mEv1nssRJfkT; Fri, 19 May 2017 20:28:06 +0000 (UTC) Received: from ash.osuosl.org (ash.osuosl.org [140.211.166.34]) by hemlock.osuosl.org (Postfix) with ESMTP id 959418A169; Fri, 19 May 2017 20:28:06 +0000 (UTC) X-Original-To: intel-wired-lan@lists.osuosl.org Delivered-To: intel-wired-lan@lists.osuosl.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by ash.osuosl.org (Postfix) with ESMTP id 038C71C037C for ; Fri, 19 May 2017 20:28:05 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id EF1478A169 for ; Fri, 19 May 2017 20:28:04 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id n-MJwwbr9FEo for ; Fri, 19 May 2017 20:28:04 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by hemlock.osuosl.org (Postfix) with ESMTPS id 3881F8A168 for ; Fri, 19 May 2017 20:28:04 +0000 (UTC) Received: from orsmga005.jf.intel.com ([10.7.209.41]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 19 May 2017 13:28:03 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.38,365,1491289200"; d="scan'208";a="103150388" Received: from anamdev.jf.intel.com ([10.166.29.110]) by orsmga005.jf.intel.com with ESMTP; 19 May 2017 13:28:03 -0700 From: Amritha Nambiar To: intel-wired-lan@lists.osuosl.org Date: Fri, 19 May 2017 17:58:23 -0700 Message-ID: <149524190301.11022.16999010740843369111.stgit@anamdev.jf.intel.com> In-Reply-To: <149524122523.11022.4541073724650541658.stgit@anamdev.jf.intel.com> References: <149524122523.11022.4541073724650541658.stgit@anamdev.jf.intel.com> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Cc: netdev@vger.kernel.org Subject: [Intel-wired-lan] [PATCH 1/4] [next-queue]net: mqprio: Introduce new hardware offload mode in mqprio for offloading full TC configurations X-BeenThere: intel-wired-lan@osuosl.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Intel Wired Ethernet Linux Kernel Driver Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-wired-lan-bounces@osuosl.org Sender: "Intel-wired-lan" This patch introduces a new hardware offload mode in mqprio which makes full use of the mqprio options, the TCs, the queue configurations and the bandwidth rates for the TCs. This is achieved by setting the value 2 for the "hw" option. This new offload mode supports new attributes for traffic class such as minimum and maximum values for bandwidth rate limits. Introduces a new datastructure 'tc_mqprio_qopt_offload' for offloading mqprio queue options and use this to be shared between the kernel and device driver. This contains a copy of the exisiting datastructure for mqprio queue options. This new datastructure can be extended when adding new attributes for traffic class such as bandwidth rate limits. The existing datastructure for mqprio queue options will be shared between the kernel and userspace. This patch enables configuring additional attributes associated with a traffic class such as minimum and maximum bandwidth rates and can be offloaded to the hardware in the new offload mode. The min and max limits for bandwidth rates are provided by the user along with the the TCs and the queue configurations when creating the mqprio qdisc. Example: # tc qdisc add dev eth0 root mqprio num_tc 2 map 0 0 0 0 1 1 1 1\ queues 4@0 4@4 min_rate 0Mbit 0Mbit max_rate 55Mbit 60Mbit hw 2 To dump the bandwidth rates: # tc qdisc show dev eth0 qdisc mqprio 804a: root tc 2 map 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 queues:(0:3) (4:7) min rates:0bit 0bit max rates:55Mbit 60Mbit Signed-off-by: Amritha Nambiar --- include/linux/netdevice.h | 2 include/net/pkt_cls.h | 7 ++ include/uapi/linux/pkt_sched.h | 13 +++ net/sched/sch_mqprio.c | 169 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 180 insertions(+), 11 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0150b2d..17b9066 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -779,6 +779,7 @@ enum { TC_SETUP_CLSFLOWER, TC_SETUP_MATCHALL, TC_SETUP_CLSBPF, + TC_SETUP_MQPRIO_EXT, }; struct tc_cls_u32_offload; @@ -791,6 +792,7 @@ struct tc_to_netdev { struct tc_cls_matchall_offload *cls_mall; struct tc_cls_bpf_offload *cls_bpf; struct tc_mqprio_qopt *mqprio; + struct tc_mqprio_qopt_offload *mqprio_qopt; }; bool egress_dev; }; diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 2c213a6..5ab8052 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -549,6 +549,13 @@ struct tc_cls_bpf_offload { u32 gen_flags; }; +struct tc_mqprio_qopt_offload { + /* struct tc_mqprio_qopt must always be the first element */ + struct tc_mqprio_qopt qopt; + u32 flags; + u64 min_rate[TC_QOPT_MAX_QUEUE]; + u64 max_rate[TC_QOPT_MAX_QUEUE]; +}; /* This structure holds cookie structure that is passed from user * to the kernel for actions and classifiers diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 099bf55..cf2a146 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -620,6 +620,7 @@ struct tc_drr_stats { enum { TC_MQPRIO_HW_OFFLOAD_NONE, /* no offload requested */ TC_MQPRIO_HW_OFFLOAD_TCS, /* offload TCs, no queue counts */ + TC_MQPRIO_HW_OFFLOAD, /* fully supported offload */ __TC_MQPRIO_HW_OFFLOAD_MAX }; @@ -633,6 +634,18 @@ struct tc_mqprio_qopt { __u16 offset[TC_QOPT_MAX_QUEUE]; }; +#define TC_MQPRIO_F_MIN_RATE 0x1 +#define TC_MQPRIO_F_MAX_RATE 0x2 + +enum { + TCA_MQPRIO_UNSPEC, + TCA_MQPRIO_MIN_RATE64, + TCA_MQPRIO_MAX_RATE64, + __TCA_MQPRIO_MAX, +}; + +#define TCA_MQPRIO_MAX (__TCA_MQPRIO_MAX - 1) + /* SFB */ enum { diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 0a4cf27..6457ec9 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -18,10 +18,13 @@ #include #include #include +#include struct mqprio_sched { struct Qdisc **qdiscs; int hw_offload; + u32 flags; + u64 min_rate[TC_QOPT_MAX_QUEUE], max_rate[TC_QOPT_MAX_QUEUE]; }; static void mqprio_destroy(struct Qdisc *sch) @@ -39,10 +42,21 @@ static void mqprio_destroy(struct Qdisc *sch) } if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { - struct tc_mqprio_qopt offload = { 0 }; - struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, - { .mqprio = &offload } }; + struct tc_mqprio_qopt_offload offload = { 0 }; + struct tc_to_netdev tc = { 0 }; + switch (priv->hw_offload) { + case TC_MQPRIO_HW_OFFLOAD_TCS: + tc.type = TC_SETUP_MQPRIO; + tc.mqprio = &offload.qopt; + break; + case TC_MQPRIO_HW_OFFLOAD: + tc.type = TC_SETUP_MQPRIO_EXT; + tc.mqprio_qopt = &offload; + break; + default: + return; + } dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc); } else { netdev_set_num_tc(dev, 0); @@ -99,6 +113,24 @@ static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt) return 0; } +static const struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = { + [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED }, + [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED }, +}; + +static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy, int len) +{ + int nested_len = nla_len(nla) - NLA_ALIGN(len); + + if (nested_len >= nla_attr_size(0)) + return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), + nested_len, policy, NULL); + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + return 0; +} + static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) { struct net_device *dev = qdisc_dev(sch); @@ -107,6 +139,10 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) struct Qdisc *qdisc; int i, err = -EOPNOTSUPP; struct tc_mqprio_qopt *qopt = NULL; + struct nlattr *tb[TCA_MQPRIO_MAX + 1]; + struct nlattr *attr; + int rem; + int len = nla_len(opt) - NLA_ALIGN(sizeof(*qopt)); BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE); BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK); @@ -124,6 +160,51 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) if (mqprio_parse_opt(dev, qopt)) return -EINVAL; + if (len > 0) { + err = parse_attr(tb, TCA_MQPRIO_MAX, opt, mqprio_policy, + sizeof(*qopt)); + if (err < 0) + return err; + + if (tb[TCA_MQPRIO_MIN_RATE64]) { + if (qopt->hw != TC_MQPRIO_HW_OFFLOAD) + return -EINVAL; + + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], + rem) { + if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64) + return -EINVAL; + + if (i >= qopt->num_tc) + return -EINVAL; + + priv->min_rate[i] = *(u64 *)nla_data(attr); + i++; + } + priv->flags |= TC_MQPRIO_F_MIN_RATE; + } + + if (tb[TCA_MQPRIO_MAX_RATE64]) { + if (qopt->hw != TC_MQPRIO_HW_OFFLOAD) + return -EINVAL; + + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], + rem) { + if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64) + return -EINVAL; + + if (i >= qopt->num_tc) + return -EINVAL; + + priv->max_rate[i] = *(u64 *)nla_data(attr); + i++; + } + priv->flags |= TC_MQPRIO_F_MAX_RATE; + } + } + /* pre-allocate qdisc, attachment can't fail */ priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), GFP_KERNEL); @@ -148,15 +229,36 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) * supplied and verified mapping */ if (qopt->hw) { - struct tc_mqprio_qopt offload = *qopt; - struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, - { .mqprio = &offload } }; + struct tc_mqprio_qopt_offload offload = {.qopt = *qopt}; + struct tc_to_netdev tc = { 0 }; + + switch (qopt->hw) { + case TC_MQPRIO_HW_OFFLOAD_TCS: + tc.type = TC_SETUP_MQPRIO; + tc.mqprio = &offload.qopt; + break; + case TC_MQPRIO_HW_OFFLOAD: + tc.type = TC_SETUP_MQPRIO_EXT; + tc.mqprio_qopt = &offload; + + offload.flags = priv->flags; + if (priv->flags & TC_MQPRIO_F_MIN_RATE) + for (i = 0; i < offload.qopt.num_tc; i++) + offload.min_rate[i] = priv->min_rate[i]; + + if (priv->flags & TC_MQPRIO_F_MAX_RATE) + for (i = 0; i < offload.qopt.num_tc; i++) + offload.max_rate[i] = priv->max_rate[i]; + break; + default: + return -EINVAL; + } err = dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc); if (err) return err; - priv->hw_offload = offload.hw; + priv->hw_offload = offload.qopt.hw; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) @@ -226,11 +328,51 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, return 0; } +static int dump_rates(struct mqprio_sched *priv, + struct tc_mqprio_qopt *opt, struct sk_buff *skb) +{ + struct nlattr *nest; + int i; + + if (priv->flags & TC_MQPRIO_F_MIN_RATE) { + nest = nla_nest_start(skb, TCA_MQPRIO_MIN_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < opt->num_tc; i++) { + if (nla_put(skb, TCA_MQPRIO_MIN_RATE64, + sizeof(priv->min_rate[i]), + &priv->min_rate[i])) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + } + + if (priv->flags & TC_MQPRIO_F_MAX_RATE) { + nest = nla_nest_start(skb, TCA_MQPRIO_MAX_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < opt->num_tc; i++) { + if (nla_put(skb, TCA_MQPRIO_MAX_RATE64, + sizeof(priv->max_rate[i]), + &priv->max_rate[i])) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + } + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -1; +} + static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) { struct net_device *dev = qdisc_dev(sch); struct mqprio_sched *priv = qdisc_priv(sch); - unsigned char *b = skb_tail_pointer(skb); + struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb); struct tc_mqprio_qopt opt = { 0 }; struct Qdisc *qdisc; unsigned int i; @@ -261,12 +403,17 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) opt.offset[i] = dev->tc_to_txq[i].offset; } - if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) + if (nla_put(skb, TCA_OPTIONS, NLA_ALIGN(sizeof(opt)), &opt)) goto nla_put_failure; - return skb->len; + if (priv->flags & TC_MQPRIO_F_MIN_RATE || + priv->flags & TC_MQPRIO_F_MAX_RATE) + if (dump_rates(priv, &opt, skb) != 0) + goto nla_put_failure; + + return nla_nest_end(skb, nla); nla_put_failure: - nlmsg_trim(skb, b); + nlmsg_trim(skb, nla); return -1; }