]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net/sched: taprio: allow user input of per-tc max SDU
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 28 Sep 2022 09:51:58 +0000 (12:51 +0300)
committerJakub Kicinski <kuba@kernel.org>
Fri, 30 Sep 2022 01:52:05 +0000 (18:52 -0700)
IEEE 802.1Q clause 12.29.1.1 "The queueMaxSDUTable structure and data
types" and 8.6.8.4 "Enhancements for scheduled traffic" talk about the
existence of a per traffic class limitation of maximum frame sizes, with
a fallback on the port-based MTU.

As far as I am able to understand, the 802.1Q Service Data Unit (SDU)
represents the MAC Service Data Unit (MSDU, i.e. L2 payload), excluding
any number of prepended VLAN headers which may be otherwise present in
the MSDU. Therefore, the queueMaxSDU is directly comparable to the
device MTU (1500 means L2 payload sizes are accepted, or frame sizes of
1518 octets, or 1522 plus one VLAN header). Drivers which offload this
are directly responsible of translating into other units of measurement.

To keep the fast path checks optimized, we keep 2 arrays in the qdisc,
one for max_sdu translated into frame length (so that it's comparable to
skb->len), and another for offloading and for dumping back to the user.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/pkt_sched.h
include/uapi/linux/pkt_sched.h
net/sched/sch_taprio.c

index 34600292fdfba78e22d85fa878cc3847ff7a32e4..38207873eda69bb708c54822b12e6b4419e3ca64 100644 (file)
@@ -160,6 +160,10 @@ struct tc_etf_qopt_offload {
        s32 queue;
 };
 
+struct tc_taprio_caps {
+       bool supports_queue_max_sdu:1;
+};
+
 struct tc_taprio_sched_entry {
        u8 command; /* TC_TAPRIO_CMD_* */
 
@@ -173,6 +177,7 @@ struct tc_taprio_qopt_offload {
        ktime_t base_time;
        u64 cycle_time;
        u64 cycle_time_extension;
+       u32 max_sdu[TC_MAX_QUEUE];
 
        size_t num_entries;
        struct tc_taprio_sched_entry entries[];
index f292b467b27fcc3e9d0320773665b0aab59e5f1b..000eec106856a4860e1b6a73dcfd5f91c0c9aa14 100644 (file)
@@ -1232,6 +1232,16 @@ enum {
 #define TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST     _BITUL(0)
 #define TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD      _BITUL(1)
 
+enum {
+       TCA_TAPRIO_TC_ENTRY_UNSPEC,
+       TCA_TAPRIO_TC_ENTRY_INDEX,              /* u32 */
+       TCA_TAPRIO_TC_ENTRY_MAX_SDU,            /* u32 */
+
+       /* add new constants above here */
+       __TCA_TAPRIO_TC_ENTRY_CNT,
+       TCA_TAPRIO_TC_ENTRY_MAX = (__TCA_TAPRIO_TC_ENTRY_CNT - 1)
+};
+
 enum {
        TCA_TAPRIO_ATTR_UNSPEC,
        TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */
@@ -1245,6 +1255,7 @@ enum {
        TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */
        TCA_TAPRIO_ATTR_FLAGS, /* u32 */
        TCA_TAPRIO_ATTR_TXTIME_DELAY, /* u32 */
+       TCA_TAPRIO_ATTR_TC_ENTRY, /* nest */
        __TCA_TAPRIO_ATTR_MAX,
 };
 
index 0bc6d90e1e51a7e13c21a539d14d5d78ce958ef1..435d866fcfa02017bc7bd68c746bafc432463d74 100644 (file)
@@ -78,6 +78,8 @@ struct taprio_sched {
        struct sched_gate_list __rcu *admin_sched;
        struct hrtimer advance_timer;
        struct list_head taprio_list;
+       u32 max_frm_len[TC_MAX_QUEUE]; /* for the fast path */
+       u32 max_sdu[TC_MAX_QUEUE]; /* for dump and offloading */
        u32 txtime_delay;
 };
 
@@ -415,6 +417,9 @@ static int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch,
                              struct Qdisc *child, struct sk_buff **to_free)
 {
        struct taprio_sched *q = qdisc_priv(sch);
+       struct net_device *dev = qdisc_dev(sch);
+       int prio = skb->priority;
+       u8 tc;
 
        /* sk_flags are only safe to use on full sockets. */
        if (skb->sk && sk_fullsock(skb->sk) && sock_flag(skb->sk, SOCK_TXTIME)) {
@@ -426,6 +431,11 @@ static int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch,
                        return qdisc_drop(skb, sch, to_free);
        }
 
+       /* Devices with full offload are expected to honor this in hardware */
+       tc = netdev_get_prio_tc_map(dev, prio);
+       if (skb->len > q->max_frm_len[tc])
+               return qdisc_drop(skb, sch, to_free);
+
        qdisc_qstats_backlog_inc(sch, skb);
        sch->q.qlen++;
 
@@ -754,6 +764,11 @@ static const struct nla_policy entry_policy[TCA_TAPRIO_SCHED_ENTRY_MAX + 1] = {
        [TCA_TAPRIO_SCHED_ENTRY_INTERVAL]  = { .type = NLA_U32 },
 };
 
+static const struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = {
+       [TCA_TAPRIO_TC_ENTRY_INDEX]        = { .type = NLA_U32 },
+       [TCA_TAPRIO_TC_ENTRY_MAX_SDU]      = { .type = NLA_U32 },
+};
+
 static const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = {
        [TCA_TAPRIO_ATTR_PRIOMAP]              = {
                .len = sizeof(struct tc_mqprio_qopt)
@@ -766,6 +781,7 @@ static const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = {
        [TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION] = { .type = NLA_S64 },
        [TCA_TAPRIO_ATTR_FLAGS]                      = { .type = NLA_U32 },
        [TCA_TAPRIO_ATTR_TXTIME_DELAY]               = { .type = NLA_U32 },
+       [TCA_TAPRIO_ATTR_TC_ENTRY]                   = { .type = NLA_NESTED },
 };
 
 static int fill_sched_entry(struct taprio_sched *q, struct nlattr **tb,
@@ -1216,7 +1232,8 @@ static int taprio_enable_offload(struct net_device *dev,
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        struct tc_taprio_qopt_offload *offload;
-       int err = 0;
+       struct tc_taprio_caps caps;
+       int tc, err = 0;
 
        if (!ops->ndo_setup_tc) {
                NL_SET_ERR_MSG(extack,
@@ -1224,6 +1241,19 @@ static int taprio_enable_offload(struct net_device *dev,
                return -EOPNOTSUPP;
        }
 
+       qdisc_offload_query_caps(dev, TC_SETUP_QDISC_TAPRIO,
+                                &caps, sizeof(caps));
+
+       if (!caps.supports_queue_max_sdu) {
+               for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
+                       if (q->max_sdu[tc]) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Device does not handle queueMaxSDU");
+                               return -EOPNOTSUPP;
+                       }
+               }
+       }
+
        offload = taprio_offload_alloc(sched->num_entries);
        if (!offload) {
                NL_SET_ERR_MSG(extack,
@@ -1233,6 +1263,9 @@ static int taprio_enable_offload(struct net_device *dev,
        offload->enable = 1;
        taprio_sched_to_offload(dev, sched, offload);
 
+       for (tc = 0; tc < TC_MAX_QUEUE; tc++)
+               offload->max_sdu[tc] = q->max_sdu[tc];
+
        err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
        if (err < 0) {
                NL_SET_ERR_MSG(extack,
@@ -1367,6 +1400,89 @@ out:
        return err;
 }
 
+static int taprio_parse_tc_entry(struct Qdisc *sch,
+                                struct nlattr *opt,
+                                u32 max_sdu[TC_QOPT_MAX_QUEUE],
+                                unsigned long *seen_tcs,
+                                struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[TCA_TAPRIO_TC_ENTRY_MAX + 1] = { };
+       struct net_device *dev = qdisc_dev(sch);
+       u32 val = 0;
+       int err, tc;
+
+       err = nla_parse_nested(tb, TCA_TAPRIO_TC_ENTRY_MAX, opt,
+                              taprio_tc_policy, extack);
+       if (err < 0)
+               return err;
+
+       if (!tb[TCA_TAPRIO_TC_ENTRY_INDEX]) {
+               NL_SET_ERR_MSG_MOD(extack, "TC entry index missing");
+               return -EINVAL;
+       }
+
+       tc = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_INDEX]);
+       if (tc >= TC_QOPT_MAX_QUEUE) {
+               NL_SET_ERR_MSG_MOD(extack, "TC entry index out of range");
+               return -ERANGE;
+       }
+
+       if (*seen_tcs & BIT(tc)) {
+               NL_SET_ERR_MSG_MOD(extack, "Duplicate TC entry");
+               return -EINVAL;
+       }
+
+       *seen_tcs |= BIT(tc);
+
+       if (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU])
+               val = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]);
+
+       if (val > dev->max_mtu) {
+               NL_SET_ERR_MSG_MOD(extack, "TC max SDU exceeds device max MTU");
+               return -ERANGE;
+       }
+
+       max_sdu[tc] = val;
+
+       return 0;
+}
+
+static int taprio_parse_tc_entries(struct Qdisc *sch,
+                                  struct nlattr *opt,
+                                  struct netlink_ext_ack *extack)
+{
+       struct taprio_sched *q = qdisc_priv(sch);
+       struct net_device *dev = qdisc_dev(sch);
+       u32 max_sdu[TC_QOPT_MAX_QUEUE];
+       unsigned long seen_tcs = 0;
+       struct nlattr *n;
+       int tc, rem;
+       int err = 0;
+
+       for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++)
+               max_sdu[tc] = q->max_sdu[tc];
+
+       nla_for_each_nested(n, opt, rem) {
+               if (nla_type(n) != TCA_TAPRIO_ATTR_TC_ENTRY)
+                       continue;
+
+               err = taprio_parse_tc_entry(sch, n, max_sdu, &seen_tcs, extack);
+               if (err)
+                       goto out;
+       }
+
+       for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
+               q->max_sdu[tc] = max_sdu[tc];
+               if (max_sdu[tc])
+                       q->max_frm_len[tc] = max_sdu[tc] + dev->hard_header_len;
+               else
+                       q->max_frm_len[tc] = U32_MAX; /* never oversized */
+       }
+
+out:
+       return err;
+}
+
 static int taprio_mqprio_cmp(const struct net_device *dev,
                             const struct tc_mqprio_qopt *mqprio)
 {
@@ -1445,6 +1561,10 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
        if (err < 0)
                return err;
 
+       err = taprio_parse_tc_entries(sch, opt, extack);
+       if (err)
+               return err;
+
        new_admin = kzalloc(sizeof(*new_admin), GFP_KERNEL);
        if (!new_admin) {
                NL_SET_ERR_MSG(extack, "Not enough memory for a new schedule");
@@ -1825,6 +1945,33 @@ error_nest:
        return -1;
 }
 
+static int taprio_dump_tc_entries(struct taprio_sched *q, struct sk_buff *skb)
+{
+       struct nlattr *n;
+       int tc;
+
+       for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
+               n = nla_nest_start(skb, TCA_TAPRIO_ATTR_TC_ENTRY);
+               if (!n)
+                       return -EMSGSIZE;
+
+               if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_INDEX, tc))
+                       goto nla_put_failure;
+
+               if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_MAX_SDU,
+                               q->max_sdu[tc]))
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, n);
+       }
+
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, n);
+       return -EMSGSIZE;
+}
+
 static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct taprio_sched *q = qdisc_priv(sch);
@@ -1863,6 +2010,9 @@ static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)
            nla_put_u32(skb, TCA_TAPRIO_ATTR_TXTIME_DELAY, q->txtime_delay))
                goto options_error;
 
+       if (taprio_dump_tc_entries(q, skb))
+               goto options_error;
+
        if (oper && dump_schedule(skb, oper))
                goto options_error;