11dfe086dSVladimir Oltean // SPDX-License-Identifier: GPL-2.0-only
21dfe086dSVladimir Oltean
31dfe086dSVladimir Oltean #include <linux/net.h>
41dfe086dSVladimir Oltean #include <linux/netdevice.h>
51dfe086dSVladimir Oltean #include <linux/netlink.h>
61dfe086dSVladimir Oltean #include <linux/types.h>
71dfe086dSVladimir Oltean #include <net/pkt_sched.h>
81dfe086dSVladimir Oltean
91dfe086dSVladimir Oltean #include "sch_mqprio_lib.h"
101dfe086dSVladimir Oltean
111dfe086dSVladimir Oltean /* Returns true if the intervals [a, b) and [c, d) overlap. */
intervals_overlap(int a,int b,int c,int d)121dfe086dSVladimir Oltean static bool intervals_overlap(int a, int b, int c, int d)
131dfe086dSVladimir Oltean {
141dfe086dSVladimir Oltean int left = max(a, c), right = min(b, d);
151dfe086dSVladimir Oltean
161dfe086dSVladimir Oltean return left < right;
171dfe086dSVladimir Oltean }
181dfe086dSVladimir Oltean
mqprio_validate_queue_counts(struct net_device * dev,const struct tc_mqprio_qopt * qopt,bool allow_overlapping_txqs,struct netlink_ext_ack * extack)191dfe086dSVladimir Oltean static int mqprio_validate_queue_counts(struct net_device *dev,
201dfe086dSVladimir Oltean const struct tc_mqprio_qopt *qopt,
211dfe086dSVladimir Oltean bool allow_overlapping_txqs,
221dfe086dSVladimir Oltean struct netlink_ext_ack *extack)
231dfe086dSVladimir Oltean {
241dfe086dSVladimir Oltean int i, j;
251dfe086dSVladimir Oltean
261dfe086dSVladimir Oltean for (i = 0; i < qopt->num_tc; i++) {
271dfe086dSVladimir Oltean unsigned int last = qopt->offset[i] + qopt->count[i];
281dfe086dSVladimir Oltean
291dfe086dSVladimir Oltean if (!qopt->count[i]) {
301dfe086dSVladimir Oltean NL_SET_ERR_MSG_FMT_MOD(extack, "No queues for TC %d",
311dfe086dSVladimir Oltean i);
321dfe086dSVladimir Oltean return -EINVAL;
331dfe086dSVladimir Oltean }
341dfe086dSVladimir Oltean
351dfe086dSVladimir Oltean /* Verify the queue count is in tx range being equal to the
361dfe086dSVladimir Oltean * real_num_tx_queues indicates the last queue is in use.
371dfe086dSVladimir Oltean */
381dfe086dSVladimir Oltean if (qopt->offset[i] >= dev->real_num_tx_queues ||
391dfe086dSVladimir Oltean last > dev->real_num_tx_queues) {
401dfe086dSVladimir Oltean NL_SET_ERR_MSG_FMT_MOD(extack,
411dfe086dSVladimir Oltean "Queues %d:%d for TC %d exceed the %d TX queues available",
421dfe086dSVladimir Oltean qopt->count[i], qopt->offset[i],
431dfe086dSVladimir Oltean i, dev->real_num_tx_queues);
441dfe086dSVladimir Oltean return -EINVAL;
451dfe086dSVladimir Oltean }
461dfe086dSVladimir Oltean
471dfe086dSVladimir Oltean if (allow_overlapping_txqs)
481dfe086dSVladimir Oltean continue;
491dfe086dSVladimir Oltean
501dfe086dSVladimir Oltean /* Verify that the offset and counts do not overlap */
511dfe086dSVladimir Oltean for (j = i + 1; j < qopt->num_tc; j++) {
521dfe086dSVladimir Oltean if (intervals_overlap(qopt->offset[i], last,
531dfe086dSVladimir Oltean qopt->offset[j],
541dfe086dSVladimir Oltean qopt->offset[j] +
551dfe086dSVladimir Oltean qopt->count[j])) {
561dfe086dSVladimir Oltean NL_SET_ERR_MSG_FMT_MOD(extack,
571dfe086dSVladimir Oltean "TC %d queues %d@%d overlap with TC %d queues %d@%d",
581dfe086dSVladimir Oltean i, qopt->count[i], qopt->offset[i],
591dfe086dSVladimir Oltean j, qopt->count[j], qopt->offset[j]);
601dfe086dSVladimir Oltean return -EINVAL;
611dfe086dSVladimir Oltean }
621dfe086dSVladimir Oltean }
631dfe086dSVladimir Oltean }
641dfe086dSVladimir Oltean
651dfe086dSVladimir Oltean return 0;
661dfe086dSVladimir Oltean }
671dfe086dSVladimir Oltean
mqprio_validate_qopt(struct net_device * dev,struct tc_mqprio_qopt * qopt,bool validate_queue_counts,bool allow_overlapping_txqs,struct netlink_ext_ack * extack)681dfe086dSVladimir Oltean int mqprio_validate_qopt(struct net_device *dev, struct tc_mqprio_qopt *qopt,
691dfe086dSVladimir Oltean bool validate_queue_counts,
701dfe086dSVladimir Oltean bool allow_overlapping_txqs,
711dfe086dSVladimir Oltean struct netlink_ext_ack *extack)
721dfe086dSVladimir Oltean {
731dfe086dSVladimir Oltean int i, err;
741dfe086dSVladimir Oltean
751dfe086dSVladimir Oltean /* Verify num_tc is not out of max range */
761dfe086dSVladimir Oltean if (qopt->num_tc > TC_MAX_QUEUE) {
771dfe086dSVladimir Oltean NL_SET_ERR_MSG(extack,
781dfe086dSVladimir Oltean "Number of traffic classes is outside valid range");
791dfe086dSVladimir Oltean return -EINVAL;
801dfe086dSVladimir Oltean }
811dfe086dSVladimir Oltean
821dfe086dSVladimir Oltean /* Verify priority mapping uses valid tcs */
831dfe086dSVladimir Oltean for (i = 0; i <= TC_BITMASK; i++) {
841dfe086dSVladimir Oltean if (qopt->prio_tc_map[i] >= qopt->num_tc) {
851dfe086dSVladimir Oltean NL_SET_ERR_MSG(extack,
861dfe086dSVladimir Oltean "Invalid traffic class in priority to traffic class mapping");
871dfe086dSVladimir Oltean return -EINVAL;
881dfe086dSVladimir Oltean }
891dfe086dSVladimir Oltean }
901dfe086dSVladimir Oltean
911dfe086dSVladimir Oltean if (validate_queue_counts) {
921dfe086dSVladimir Oltean err = mqprio_validate_queue_counts(dev, qopt,
931dfe086dSVladimir Oltean allow_overlapping_txqs,
941dfe086dSVladimir Oltean extack);
951dfe086dSVladimir Oltean if (err)
961dfe086dSVladimir Oltean return err;
971dfe086dSVladimir Oltean }
981dfe086dSVladimir Oltean
991dfe086dSVladimir Oltean return 0;
1001dfe086dSVladimir Oltean }
1011dfe086dSVladimir Oltean EXPORT_SYMBOL_GPL(mqprio_validate_qopt);
1021dfe086dSVladimir Oltean
mqprio_qopt_reconstruct(struct net_device * dev,struct tc_mqprio_qopt * qopt)1039dd6ad67SVladimir Oltean void mqprio_qopt_reconstruct(struct net_device *dev, struct tc_mqprio_qopt *qopt)
1049dd6ad67SVladimir Oltean {
1059dd6ad67SVladimir Oltean int tc, num_tc = netdev_get_num_tc(dev);
1069dd6ad67SVladimir Oltean
1079dd6ad67SVladimir Oltean qopt->num_tc = num_tc;
1089dd6ad67SVladimir Oltean memcpy(qopt->prio_tc_map, dev->prio_tc_map, sizeof(qopt->prio_tc_map));
1099dd6ad67SVladimir Oltean
1109dd6ad67SVladimir Oltean for (tc = 0; tc < num_tc; tc++) {
1119dd6ad67SVladimir Oltean qopt->count[tc] = dev->tc_to_txq[tc].count;
1129dd6ad67SVladimir Oltean qopt->offset[tc] = dev->tc_to_txq[tc].offset;
1139dd6ad67SVladimir Oltean }
1149dd6ad67SVladimir Oltean }
1159dd6ad67SVladimir Oltean EXPORT_SYMBOL_GPL(mqprio_qopt_reconstruct);
1169dd6ad67SVladimir Oltean
mqprio_fp_to_offload(u32 fp[TC_QOPT_MAX_QUEUE],struct tc_mqprio_qopt_offload * mqprio)117*f62af20bSVladimir Oltean void mqprio_fp_to_offload(u32 fp[TC_QOPT_MAX_QUEUE],
118*f62af20bSVladimir Oltean struct tc_mqprio_qopt_offload *mqprio)
119*f62af20bSVladimir Oltean {
120*f62af20bSVladimir Oltean unsigned long preemptible_tcs = 0;
121*f62af20bSVladimir Oltean int tc;
122*f62af20bSVladimir Oltean
123*f62af20bSVladimir Oltean for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++)
124*f62af20bSVladimir Oltean if (fp[tc] == TC_FP_PREEMPTIBLE)
125*f62af20bSVladimir Oltean preemptible_tcs |= BIT(tc);
126*f62af20bSVladimir Oltean
127*f62af20bSVladimir Oltean mqprio->preemptible_tcs = preemptible_tcs;
128*f62af20bSVladimir Oltean }
129*f62af20bSVladimir Oltean EXPORT_SYMBOL_GPL(mqprio_fp_to_offload);
130*f62af20bSVladimir Oltean
1311dfe086dSVladimir Oltean MODULE_LICENSE("GPL");
132