1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip Sparx5 Switch driver 3 * 4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include <net/pkt_cls.h> 8 #include <net/pkt_sched.h> 9 10 #include "sparx5_tc.h" 11 #include "sparx5_main.h" 12 #include "sparx5_qos.h" 13 14 /* tc block handling */ 15 static LIST_HEAD(sparx5_block_cb_list); 16 17 static int sparx5_tc_block_cb(enum tc_setup_type type, 18 void *type_data, 19 void *cb_priv, bool ingress) 20 { 21 struct net_device *ndev = cb_priv; 22 23 switch (type) { 24 case TC_SETUP_CLSMATCHALL: 25 return sparx5_tc_matchall(ndev, type_data, ingress); 26 case TC_SETUP_CLSFLOWER: 27 return sparx5_tc_flower(ndev, type_data, ingress); 28 default: 29 return -EOPNOTSUPP; 30 } 31 } 32 33 static int sparx5_tc_block_cb_ingress(enum tc_setup_type type, 34 void *type_data, 35 void *cb_priv) 36 { 37 return sparx5_tc_block_cb(type, type_data, cb_priv, true); 38 } 39 40 static int sparx5_tc_block_cb_egress(enum tc_setup_type type, 41 void *type_data, 42 void *cb_priv) 43 { 44 return sparx5_tc_block_cb(type, type_data, cb_priv, false); 45 } 46 47 static int sparx5_tc_setup_block(struct net_device *ndev, 48 struct flow_block_offload *fbo) 49 { 50 flow_setup_cb_t *cb; 51 52 if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 53 cb = sparx5_tc_block_cb_ingress; 54 else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 55 cb = sparx5_tc_block_cb_egress; 56 else 57 return -EOPNOTSUPP; 58 59 return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list, 60 cb, ndev, ndev, false); 61 } 62 63 static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer, 64 u32 *idx) 65 { 66 if (parent == TC_H_ROOT) { 67 *layer = 2; 68 *idx = portno; 69 } else { 70 u32 queue = TC_H_MIN(parent) - 1; 71 *layer = 0; 72 *idx = SPX5_HSCH_L0_GET_IDX(portno, queue); 73 } 74 } 75 76 static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev, 77 struct tc_mqprio_qopt_offload *m) 78 { 79 m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; 80 81 if (m->qopt.num_tc == 0) 82 return sparx5_tc_mqprio_del(ndev); 83 else 84 return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc); 85 } 86 87 static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev, 88 struct tc_tbf_qopt_offload *qopt) 89 { 90 struct sparx5_port *port = netdev_priv(ndev); 91 u32 layer, se_idx; 92 93 sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer, 94 &se_idx); 95 96 switch (qopt->command) { 97 case TC_TBF_REPLACE: 98 return sparx5_tc_tbf_add(port, &qopt->replace_params, layer, 99 se_idx); 100 case TC_TBF_DESTROY: 101 return sparx5_tc_tbf_del(port, layer, se_idx); 102 case TC_TBF_STATS: 103 return -EOPNOTSUPP; 104 default: 105 return -EOPNOTSUPP; 106 } 107 108 return -EOPNOTSUPP; 109 } 110 111 static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev, 112 struct tc_ets_qopt_offload *qopt) 113 { 114 struct tc_ets_qopt_offload_replace_params *params = 115 &qopt->replace_params; 116 struct sparx5_port *port = netdev_priv(ndev); 117 int i; 118 119 /* Only allow ets on ports */ 120 if (qopt->parent != TC_H_ROOT) 121 return -EOPNOTSUPP; 122 123 switch (qopt->command) { 124 case TC_ETS_REPLACE: 125 126 /* We support eight priorities */ 127 if (params->bands != SPX5_PRIOS) 128 return -EOPNOTSUPP; 129 130 /* Sanity checks */ 131 for (i = 0; i < SPX5_PRIOS; ++i) { 132 /* Priority map is *always* reverse e.g: 7 6 5 .. 0 */ 133 if (params->priomap[i] != (7 - i)) 134 return -EOPNOTSUPP; 135 /* Throw an error if we receive zero weights by tc */ 136 if (params->quanta[i] && params->weights[i] == 0) { 137 pr_err("Invalid ets configuration; band %d has weight zero", 138 i); 139 return -EINVAL; 140 } 141 } 142 143 return sparx5_tc_ets_add(port, params); 144 case TC_ETS_DESTROY: 145 146 return sparx5_tc_ets_del(port); 147 case TC_ETS_GRAFT: 148 return -EOPNOTSUPP; 149 150 default: 151 return -EOPNOTSUPP; 152 } 153 154 return -EOPNOTSUPP; 155 } 156 157 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, 158 void *type_data) 159 { 160 switch (type) { 161 case TC_SETUP_BLOCK: 162 return sparx5_tc_setup_block(ndev, type_data); 163 case TC_SETUP_QDISC_MQPRIO: 164 return sparx5_tc_setup_qdisc_mqprio(ndev, type_data); 165 case TC_SETUP_QDISC_TBF: 166 return sparx5_tc_setup_qdisc_tbf(ndev, type_data); 167 case TC_SETUP_QDISC_ETS: 168 return sparx5_tc_setup_qdisc_ets(ndev, type_data); 169 default: 170 return -EOPNOTSUPP; 171 } 172 173 return 0; 174 } 175