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