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. */
12static 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
19static 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
68int 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}
101EXPORT_SYMBOL_GPL(mqprio_validate_qopt);
102
103void 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}
115EXPORT_SYMBOL_GPL(mqprio_qopt_reconstruct);
116
117void 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}
129EXPORT_SYMBOL_GPL(mqprio_fp_to_offload);
130
131MODULE_LICENSE("GPL");
132MODULE_DESCRIPTION("Shared mqprio qdisc code currently between taprio and mqprio");
133