1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <linux/net.h>
4 #include <linux/netdevice.h>
5 #include <linux/netlink.h>
6 #include <linux/types.h>
7 #include <net/pkt_sched.h>
8 
9 #include "sch_mqprio_lib.h"
10 
11 /* Returns true if the intervals [a, b) and [c, d) overlap. */
intervals_overlap(int a,int b,int c,int d)12 static bool intervals_overlap(int a, int b, int c, int d)
13 {
14 	int left = max(a, c), right = min(b, d);
15 
16 	return left < right;
17 }
18 
mqprio_validate_queue_counts(struct net_device * dev,const struct tc_mqprio_qopt * qopt,bool allow_overlapping_txqs,struct netlink_ext_ack * extack)19 static int mqprio_validate_queue_counts(struct net_device *dev,
20 					const struct tc_mqprio_qopt *qopt,
21 					bool allow_overlapping_txqs,
22 					struct netlink_ext_ack *extack)
23 {
24 	int i, j;
25 
26 	for (i = 0; i < qopt->num_tc; i++) {
27 		unsigned int last = qopt->offset[i] + qopt->count[i];
28 
29 		if (!qopt->count[i]) {
30 			NL_SET_ERR_MSG_FMT_MOD(extack, "No queues for TC %d",
31 					       i);
32 			return -EINVAL;
33 		}
34 
35 		/* Verify the queue count is in tx range being equal to the
36 		 * real_num_tx_queues indicates the last queue is in use.
37 		 */
38 		if (qopt->offset[i] >= dev->real_num_tx_queues ||
39 		    last > dev->real_num_tx_queues) {
40 			NL_SET_ERR_MSG_FMT_MOD(extack,
41 					       "Queues %d:%d for TC %d exceed the %d TX queues available",
42 					       qopt->count[i], qopt->offset[i],
43 					       i, dev->real_num_tx_queues);
44 			return -EINVAL;
45 		}
46 
47 		if (allow_overlapping_txqs)
48 			continue;
49 
50 		/* Verify that the offset and counts do not overlap */
51 		for (j = i + 1; j < qopt->num_tc; j++) {
52 			if (intervals_overlap(qopt->offset[i], last,
53 					      qopt->offset[j],
54 					      qopt->offset[j] +
55 					      qopt->count[j])) {
56 				NL_SET_ERR_MSG_FMT_MOD(extack,
57 						       "TC %d queues %d@%d overlap with TC %d queues %d@%d",
58 						       i, qopt->count[i], qopt->offset[i],
59 						       j, qopt->count[j], qopt->offset[j]);
60 				return -EINVAL;
61 			}
62 		}
63 	}
64 
65 	return 0;
66 }
67 
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)68 int mqprio_validate_qopt(struct net_device *dev, struct tc_mqprio_qopt *qopt,
69 			 bool validate_queue_counts,
70 			 bool allow_overlapping_txqs,
71 			 struct netlink_ext_ack *extack)
72 {
73 	int i, err;
74 
75 	/* Verify num_tc is not out of max range */
76 	if (qopt->num_tc > TC_MAX_QUEUE) {
77 		NL_SET_ERR_MSG(extack,
78 			       "Number of traffic classes is outside valid range");
79 		return -EINVAL;
80 	}
81 
82 	/* Verify priority mapping uses valid tcs */
83 	for (i = 0; i <= TC_BITMASK; i++) {
84 		if (qopt->prio_tc_map[i] >= qopt->num_tc) {
85 			NL_SET_ERR_MSG(extack,
86 				       "Invalid traffic class in priority to traffic class mapping");
87 			return -EINVAL;
88 		}
89 	}
90 
91 	if (validate_queue_counts) {
92 		err = mqprio_validate_queue_counts(dev, qopt,
93 						   allow_overlapping_txqs,
94 						   extack);
95 		if (err)
96 			return err;
97 	}
98 
99 	return 0;
100 }
101 EXPORT_SYMBOL_GPL(mqprio_validate_qopt);
102 
mqprio_qopt_reconstruct(struct net_device * dev,struct tc_mqprio_qopt * qopt)103 void mqprio_qopt_reconstruct(struct net_device *dev, struct tc_mqprio_qopt *qopt)
104 {
105 	int tc, num_tc = netdev_get_num_tc(dev);
106 
107 	qopt->num_tc = num_tc;
108 	memcpy(qopt->prio_tc_map, dev->prio_tc_map, sizeof(qopt->prio_tc_map));
109 
110 	for (tc = 0; tc < num_tc; tc++) {
111 		qopt->count[tc] = dev->tc_to_txq[tc].count;
112 		qopt->offset[tc] = dev->tc_to_txq[tc].offset;
113 	}
114 }
115 EXPORT_SYMBOL_GPL(mqprio_qopt_reconstruct);
116 
mqprio_fp_to_offload(u32 fp[TC_QOPT_MAX_QUEUE],struct tc_mqprio_qopt_offload * mqprio)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 
131 MODULE_LICENSE("GPL");
132 MODULE_DESCRIPTION("Shared mqprio qdisc code currently between taprio and mqprio");
133