1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip VCAP API 3 * 4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include "sparx5_tc.h" 8 #include "vcap_api.h" 9 #include "vcap_api_client.h" 10 #include "sparx5_main_regs.h" 11 #include "sparx5_main.h" 12 #include "sparx5_vcap_impl.h" 13 14 static struct sparx5_mall_entry * 15 sparx5_tc_matchall_entry_find(struct list_head *entries, unsigned long cookie) 16 { 17 struct sparx5_mall_entry *entry; 18 19 list_for_each_entry(entry, entries, list) { 20 if (entry->cookie == cookie) 21 return entry; 22 } 23 24 return NULL; 25 } 26 27 static void sparx5_tc_matchall_parse_action(struct sparx5_port *port, 28 struct sparx5_mall_entry *entry, 29 struct flow_action_entry *action, 30 bool ingress, 31 unsigned long cookie) 32 { 33 entry->port = port; 34 entry->type = action->id; 35 entry->ingress = ingress; 36 entry->cookie = cookie; 37 } 38 39 static void 40 sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry *entry, 41 struct flow_action_entry *action) 42 { 43 entry->mirror.port = netdev_priv(action->dev); 44 } 45 46 static int sparx5_tc_matchall_replace(struct net_device *ndev, 47 struct tc_cls_matchall_offload *tmo, 48 bool ingress) 49 { 50 struct sparx5_port *port = netdev_priv(ndev); 51 struct sparx5_mall_entry *mall_entry; 52 struct flow_action_entry *action; 53 struct sparx5 *sparx5; 54 int err; 55 56 if (!flow_offload_has_one_action(&tmo->rule->action)) { 57 NL_SET_ERR_MSG_MOD(tmo->common.extack, 58 "Only one action per filter is supported"); 59 return -EOPNOTSUPP; 60 } 61 action = &tmo->rule->action.entries[0]; 62 63 mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 64 if (!mall_entry) 65 return -ENOMEM; 66 67 sparx5_tc_matchall_parse_action(port, 68 mall_entry, 69 action, 70 ingress, 71 tmo->cookie); 72 73 sparx5 = port->sparx5; 74 switch (action->id) { 75 case FLOW_ACTION_MIRRED: 76 sparx5_tc_matchall_parse_mirror_action(mall_entry, action); 77 err = sparx5_mirror_add(mall_entry); 78 if (err) { 79 switch (err) { 80 case -EEXIST: 81 NL_SET_ERR_MSG_MOD(tmo->common.extack, 82 "Mirroring already exists"); 83 break; 84 case -EINVAL: 85 NL_SET_ERR_MSG_MOD(tmo->common.extack, 86 "Cannot mirror a monitor port"); 87 break; 88 case -ENOENT: 89 NL_SET_ERR_MSG_MOD(tmo->common.extack, 90 "No more mirror probes available"); 91 break; 92 default: 93 NL_SET_ERR_MSG_MOD(tmo->common.extack, 94 "Unknown error"); 95 break; 96 } 97 return err; 98 } 99 /* Get baseline stats for this port */ 100 sparx5_mirror_stats(mall_entry, &tmo->stats); 101 break; 102 case FLOW_ACTION_GOTO: 103 err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, 104 tmo->common.chain_index, 105 action->chain_index, tmo->cookie, 106 true); 107 if (err == -EFAULT) { 108 NL_SET_ERR_MSG_MOD(tmo->common.extack, 109 "Unsupported goto chain"); 110 return -EOPNOTSUPP; 111 } 112 if (err == -EADDRINUSE) { 113 NL_SET_ERR_MSG_MOD(tmo->common.extack, 114 "VCAP already enabled"); 115 return -EOPNOTSUPP; 116 } 117 if (err == -EADDRNOTAVAIL) { 118 NL_SET_ERR_MSG_MOD(tmo->common.extack, 119 "Already matching this chain"); 120 return -EOPNOTSUPP; 121 } 122 if (err) { 123 NL_SET_ERR_MSG_MOD(tmo->common.extack, 124 "Could not enable VCAP lookups"); 125 return err; 126 } 127 break; 128 default: 129 NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); 130 return -EOPNOTSUPP; 131 } 132 133 list_add_tail(&mall_entry->list, &sparx5->mall_entries); 134 135 return 0; 136 } 137 138 static int sparx5_tc_matchall_destroy(struct net_device *ndev, 139 struct tc_cls_matchall_offload *tmo, 140 bool ingress) 141 { 142 struct sparx5_port *port = netdev_priv(ndev); 143 struct sparx5 *sparx5 = port->sparx5; 144 struct sparx5_mall_entry *entry; 145 int err = 0; 146 147 entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries, 148 tmo->cookie); 149 if (!entry) 150 return -ENOENT; 151 152 if (entry->type == FLOW_ACTION_MIRRED) { 153 sparx5_mirror_del(entry); 154 } else if (entry->type == FLOW_ACTION_GOTO) { 155 err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, 156 0, 0, tmo->cookie, false); 157 } else { 158 NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); 159 err = -EOPNOTSUPP; 160 } 161 162 list_del(&entry->list); 163 164 return err; 165 } 166 167 static int sparx5_tc_matchall_stats(struct net_device *ndev, 168 struct tc_cls_matchall_offload *tmo, 169 bool ingress) 170 { 171 struct sparx5_port *port = netdev_priv(ndev); 172 struct sparx5 *sparx5 = port->sparx5; 173 struct sparx5_mall_entry *entry; 174 175 entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries, 176 tmo->cookie); 177 if (!entry) 178 return -ENOENT; 179 180 if (entry->type == FLOW_ACTION_MIRRED) { 181 sparx5_mirror_stats(entry, &tmo->stats); 182 } else { 183 NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); 184 return -EOPNOTSUPP; 185 } 186 187 return 0; 188 } 189 190 int sparx5_tc_matchall(struct net_device *ndev, 191 struct tc_cls_matchall_offload *tmo, 192 bool ingress) 193 { 194 switch (tmo->command) { 195 case TC_CLSMATCHALL_REPLACE: 196 return sparx5_tc_matchall_replace(ndev, tmo, ingress); 197 case TC_CLSMATCHALL_DESTROY: 198 return sparx5_tc_matchall_destroy(ndev, tmo, ingress); 199 case TC_CLSMATCHALL_STATS: 200 return sparx5_tc_matchall_stats(ndev, tmo, ingress); 201 default: 202 return -EOPNOTSUPP; 203 } 204 } 205