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. */ 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 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 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 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 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