xref: /openbmc/linux/net/sched/sch_mqprio_lib.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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