History

net/sched: mqprio: allow per-TC user input of FP adminStatus

IEEE 802.1Q-2018 clause 6.7.2 Frame preemption specifies that each packet priority can be assigned to a "frame preemption status" value of either "express" or "preemptible". Express priorities are transmitted by the local device through the eMAC, and preemptible priorities through the pMAC (the concepts of eMAC and pMAC come from the 802.3 MAC Merge layer). The FP adminStatus is defined per packet priority, but 802.1Q clause 12.30.1.1.1 framePreemptionAdminStatus also says that: | Priorities that all map to the same traffic class should be | constrained to use the same value of preemption status. It is impossible to ignore the cognitive dissonance in the standard here, because it practically means that the FP adminStatus only takes distinct values per traffic class, even though it is defined per priority. I can see no valid use case which is prevented by having the kernel take the FP adminStatus as input per traffic class (what we do here). In addition, this also enforces the above constraint by construction. User space network managers which wish to expose FP adminStatus per priority are free to do so; they must only observe the prio_tc_map of the netdev (which presumably is also under their control, when constructing the mqprio netlink attributes). The reason for configuring frame preemption as a property of the Qdisc layer is that the information about "preemptible TCs" is closest to the place which handles the num_tc and prio_tc_map of the netdev. If the UAPI would have been any other layer, it would be unclear what to do with the FP information when num_tc collapses to 0. A key assumption is that only mqprio/taprio change the num_tc and prio_tc_map of the netdev. Not sure if that's a great assumption to make. Having FP in tc-mqprio can be seen as an implementation of the use case defined in 802.1Q Annex S.2 "Preemption used in isolation". There will be a separate implementation of FP in tc-taprio, for the other use cases.

Vladimir Oltean committed 3 years ago

Commit f62af201 parent c54876c5 files · +160 −1Browse files

Unified · Split

5 files
  1. include/net/pkt_sched.h +1 −0
  2. include/uapi/linux/pkt_sched.h +16 −0
  3. net/sched/sch_mqprio.c +127 −1
  4. net/sched/sch_mqprio_lib.c +14 −0
  5. net/sched/sch_mqprio_lib.h +2 −0
include/net/pkt_sched.h+1−0
@@ -172,6 +172,7 @@ struct tc_mqprio_qopt_offload { · 171 unmodified lines
172172 u32 flags;
173173 u64 min_rate[TC_QOPT_MAX_QUEUE];
174174 u64 max_rate[TC_QOPT_MAX_QUEUE];
175+ unsigned long preemptible_tcs;
175176 };
176177
177178 struct tc_taprio_caps {
include/uapi/linux/pkt_sched.h+16−0
@@ -719,6 +719,11 @@ enum { · 718 unmodified lines
719719
720720 #define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1)
721721
722+enum {
723+ TC_FP_EXPRESS = 1,
724+ TC_FP_PREEMPTIBLE = 2,
725+};
726+
722727 struct tc_mqprio_qopt {
723728 __u8 num_tc;
724729 __u8 prio_tc_map[TC_QOPT_BITMASK + 1];
@@ -732,12 +737,23 @@ struct tc_mqprio_qopt { · 7 unmodified lines
732737 #define TC_MQPRIO_F_MIN_RATE 0x4
733738 #define TC_MQPRIO_F_MAX_RATE 0x8
734739
740+enum {
741+ TCA_MQPRIO_TC_ENTRY_UNSPEC,
742+ TCA_MQPRIO_TC_ENTRY_INDEX, /* u32 */
743+ TCA_MQPRIO_TC_ENTRY_FP, /* u32 */
744+
745+ /* add new constants above here */
746+ __TCA_MQPRIO_TC_ENTRY_CNT,
747+ TCA_MQPRIO_TC_ENTRY_MAX = (__TCA_MQPRIO_TC_ENTRY_CNT - 1)
748+};
749+
735750 enum {
736751 TCA_MQPRIO_UNSPEC,
737752 TCA_MQPRIO_MODE,
738753 TCA_MQPRIO_SHAPER,
739754 TCA_MQPRIO_MIN_RATE64,
740755 TCA_MQPRIO_MAX_RATE64,
756+ TCA_MQPRIO_TC_ENTRY,
741757 __TCA_MQPRIO_MAX,
742758 };
743759
net/sched/sch_mqprio.c+127−1
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 * Copyright (c) 2010 John Fastabend <john.r.fastabend@intel.com>
66 */
77
8+#include <linux/ethtool_netlink.h>
89 #include <linux/types.h>
910 #include <linux/slab.h>
1011 #include <linux/kernel.h>
@@ -27,6 +28,7 @@ struct mqprio_sched { · 16 unmodified lines
2728 u32 flags;
2829 u64 min_rate[TC_QOPT_MAX_QUEUE];
2930 u64 max_rate[TC_QOPT_MAX_QUEUE];
31+ u32 fp[TC_QOPT_MAX_QUEUE];
3032 };
3133
3234 static int mqprio_enable_offload(struct Qdisc *sch,
@@ -63,6 +65,8 @@ static int mqprio_enable_offload(struct Qdisc *sch, · 30 unmodified lines
6365 return -EINVAL;
6466 }
6567
68+ mqprio_fp_to_offload(priv->fp, &mqprio);
69+
6670 err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQPRIO,
6771 &mqprio);
6872 if (err)
@@ -145,13 +149,95 @@ static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt, · 76 unmodified lines
145149 return 0;
146150 }
147151
152+static const struct
153+nla_policy mqprio_tc_entry_policy[TCA_MQPRIO_TC_ENTRY_MAX + 1] = {
154+ [TCA_MQPRIO_TC_ENTRY_INDEX] = NLA_POLICY_MAX(NLA_U32,
155+ TC_QOPT_MAX_QUEUE),
156+ [TCA_MQPRIO_TC_ENTRY_FP] = NLA_POLICY_RANGE(NLA_U32,
157+ TC_FP_EXPRESS,
158+ TC_FP_PREEMPTIBLE),
159+};
160+
148161 static const struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = {
149162 [TCA_MQPRIO_MODE] = { .len = sizeof(u16) },
150163 [TCA_MQPRIO_SHAPER] = { .len = sizeof(u16) },
151164 [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED },
152165 [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED },
166+ [TCA_MQPRIO_TC_ENTRY] = { .type = NLA_NESTED },
153167 };
154168
169+static int mqprio_parse_tc_entry(u32 fp[TC_QOPT_MAX_QUEUE],
170+ struct nlattr *opt,
171+ unsigned long *seen_tcs,
172+ struct netlink_ext_ack *extack)
173+{
174+ struct nlattr *tb[TCA_MQPRIO_TC_ENTRY_MAX + 1];
175+ int err, tc;
176+
177+ err = nla_parse_nested(tb, TCA_MQPRIO_TC_ENTRY_MAX, opt,
178+ mqprio_tc_entry_policy, extack);
179+ if (err < 0)
180+ return err;
181+
182+ if (NL_REQ_ATTR_CHECK(extack, opt, tb, TCA_MQPRIO_TC_ENTRY_INDEX)) {
183+ NL_SET_ERR_MSG(extack, "TC entry index missing");
184+ return -EINVAL;
185+ }
186+
187+ tc = nla_get_u32(tb[TCA_MQPRIO_TC_ENTRY_INDEX]);
188+ if (*seen_tcs & BIT(tc)) {
189+ NL_SET_ERR_MSG_ATTR(extack, tb[TCA_MQPRIO_TC_ENTRY_INDEX],
190+ "Duplicate tc entry");
191+ return -EINVAL;
192+ }
193+
194+ *seen_tcs |= BIT(tc);
195+
196+ if (tb[TCA_MQPRIO_TC_ENTRY_FP])
197+ fp[tc] = nla_get_u32(tb[TCA_MQPRIO_TC_ENTRY_FP]);
198+
199+ return 0;
200+}
201+
202+static int mqprio_parse_tc_entries(struct Qdisc *sch, struct nlattr *nlattr_opt,
203+ int nlattr_opt_len,
204+ struct netlink_ext_ack *extack)
205+{
206+ struct mqprio_sched *priv = qdisc_priv(sch);
207+ struct net_device *dev = qdisc_dev(sch);
208+ bool have_preemption = false;
209+ unsigned long seen_tcs = 0;
210+ u32 fp[TC_QOPT_MAX_QUEUE];
211+ struct nlattr *n;
212+ int tc, rem;
213+ int err = 0;
214+
215+ for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++)
216+ fp[tc] = priv->fp[tc];
217+
218+ nla_for_each_attr(n, nlattr_opt, nlattr_opt_len, rem) {
219+ if (nla_type(n) != TCA_MQPRIO_TC_ENTRY)
220+ continue;
221+
222+ err = mqprio_parse_tc_entry(fp, n, &seen_tcs, extack);
223+ if (err)
224+ goto out;
225+ }
226+
227+ for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
228+ priv->fp[tc] = fp[tc];
229+ if (fp[tc] == TC_FP_PREEMPTIBLE)
230+ have_preemption = true;
231+ }
232+
233+ if (have_preemption && !ethtool_dev_mm_supported(dev)) {
234+ NL_SET_ERR_MSG(extack, "Device does not support preemption");
235+ return -EOPNOTSUPP;
236+ }
237+out:
238+ return err;
239+}
240+
155241 /* Parse the other netlink attributes that represent the payload of
156242 * TCA_OPTIONS, which are appended right after struct tc_mqprio_qopt.
157243 */
@@ -234,6 +320,13 @@ static int mqprio_parse_nlattr(struct Qdisc *sch, struct tc_mqprio_qopt *qopt, · 76 unmodified lines
234320 priv->flags |= TC_MQPRIO_F_MAX_RATE;
235321 }
236322
323+ if (tb[TCA_MQPRIO_TC_ENTRY]) {
324+ err = mqprio_parse_tc_entries(sch, nlattr_opt, nlattr_opt_len,
325+ extack);
326+ if (err)
327+ return err;
328+ }
329+
237330 return 0;
238331 }
239332
@@ -247,7 +340,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt, · 7 unmodified lines
247340 int i, err = -EOPNOTSUPP;
248341 struct tc_mqprio_qopt *qopt = NULL;
249342 struct tc_mqprio_caps caps;
250− int len;
343+ int len, tc;
251344
252345 BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE);
253346 BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK);
@@ -265,6 +358,9 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt, · 11 unmodified lines
265358 if (!opt || nla_len(opt) < sizeof(*qopt))
266359 return -EINVAL;
267360
361+ for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++)
362+ priv->fp[tc] = TC_FP_EXPRESS;
363+
268364 qdisc_offload_query_caps(dev, TC_SETUP_QDISC_MQPRIO,
269365 &caps, sizeof(caps));
270366
@@ -415,6 +511,33 @@ nla_put_failure: · 144 unmodified lines
415511 return -1;
416512 }
417513
514+static int mqprio_dump_tc_entries(struct mqprio_sched *priv,
515+ struct sk_buff *skb)
516+{
517+ struct nlattr *n;
518+ int tc;
519+
520+ for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
521+ n = nla_nest_start(skb, TCA_MQPRIO_TC_ENTRY);
522+ if (!n)
523+ return -EMSGSIZE;
524+
525+ if (nla_put_u32(skb, TCA_MQPRIO_TC_ENTRY_INDEX, tc))
526+ goto nla_put_failure;
527+
528+ if (nla_put_u32(skb, TCA_MQPRIO_TC_ENTRY_FP, priv->fp[tc]))
529+ goto nla_put_failure;
530+
531+ nla_nest_end(skb, n);
532+ }
533+
534+ return 0;
535+
536+nla_put_failure:
537+ nla_nest_cancel(skb, n);
538+ return -EMSGSIZE;
539+}
540+
418541 static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
419542 {
420543 struct net_device *dev = qdisc_dev(sch);
@@ -465,6 +588,9 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) · 44 unmodified lines
465588 (dump_rates(priv, &opt, skb) != 0))
466589 goto nla_put_failure;
467590
591+ if (mqprio_dump_tc_entries(priv, skb))
592+ goto nla_put_failure;
593+
468594 return nla_nest_end(skb, nla);
469595 nla_put_failure:
470596 nlmsg_trim(skb, nla);
net/sched/sch_mqprio_lib.c+14−0
@@ -114,4 +114,18 @@ void mqprio_qopt_reconstruct(struct net_device *dev, struct tc_mqprio_qopt *qopt · 113 unmodified lines
114114 }
115115 EXPORT_SYMBOL_GPL(mqprio_qopt_reconstruct);
116116
117+void mqprio_fp_to_offload(u32 fp[TC_QOPT_MAX_QUEUE],
118+ struct tc_mqprio_qopt_offload *mqprio)
119+{
120+ unsigned long preemptible_tcs = 0;
121+ int tc;
122+
123+ for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++)
124+ if (fp[tc] == TC_FP_PREEMPTIBLE)
125+ preemptible_tcs |= BIT(tc);
126+
127+ mqprio->preemptible_tcs = preemptible_tcs;
128+}
129+EXPORT_SYMBOL_GPL(mqprio_fp_to_offload);
130+
117131 MODULE_LICENSE("GPL");
net/sched/sch_mqprio_lib.h+2−0
@@ -14,5 +14,7 @@ int mqprio_validate_qopt(struct net_device *dev, struct tc_mqprio_qopt *qopt, · 13 unmodified lines
1414 struct netlink_ext_ack *extack);
1515 void mqprio_qopt_reconstruct(struct net_device *dev,
1616 struct tc_mqprio_qopt *qopt);
17+void mqprio_fp_to_offload(u32 fp[TC_QOPT_MAX_QUEUE],
18+ struct tc_mqprio_qopt_offload *mqprio);
1719
1820 #endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t