1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/list.h> 6 7 #include "prestera.h" 8 #include "prestera_acl.h" 9 #include "prestera_flow.h" 10 #include "prestera_span.h" 11 #include "prestera_flower.h" 12 13 static LIST_HEAD(prestera_block_cb_list); 14 15 static int prestera_flow_block_mall_cb(struct prestera_flow_block *block, 16 struct tc_cls_matchall_offload *f) 17 { 18 switch (f->command) { 19 case TC_CLSMATCHALL_REPLACE: 20 return prestera_span_replace(block, f); 21 case TC_CLSMATCHALL_DESTROY: 22 prestera_span_destroy(block); 23 return 0; 24 default: 25 return -EOPNOTSUPP; 26 } 27 } 28 29 static int prestera_flow_block_flower_cb(struct prestera_flow_block *block, 30 struct flow_cls_offload *f) 31 { 32 if (f->common.chain_index != 0) 33 return -EOPNOTSUPP; 34 35 switch (f->command) { 36 case FLOW_CLS_REPLACE: 37 return prestera_flower_replace(block, f); 38 case FLOW_CLS_DESTROY: 39 prestera_flower_destroy(block, f); 40 return 0; 41 case FLOW_CLS_STATS: 42 return prestera_flower_stats(block, f); 43 default: 44 return -EOPNOTSUPP; 45 } 46 } 47 48 static int prestera_flow_block_cb(enum tc_setup_type type, 49 void *type_data, void *cb_priv) 50 { 51 struct prestera_flow_block *block = cb_priv; 52 53 switch (type) { 54 case TC_SETUP_CLSFLOWER: 55 return prestera_flow_block_flower_cb(block, type_data); 56 case TC_SETUP_CLSMATCHALL: 57 return prestera_flow_block_mall_cb(block, type_data); 58 default: 59 return -EOPNOTSUPP; 60 } 61 } 62 63 static void prestera_flow_block_destroy(void *cb_priv) 64 { 65 struct prestera_flow_block *block = cb_priv; 66 67 WARN_ON(!list_empty(&block->binding_list)); 68 69 kfree(block); 70 } 71 72 static struct prestera_flow_block * 73 prestera_flow_block_create(struct prestera_switch *sw, struct net *net) 74 { 75 struct prestera_flow_block *block; 76 77 block = kzalloc(sizeof(*block), GFP_KERNEL); 78 if (!block) 79 return NULL; 80 81 INIT_LIST_HEAD(&block->binding_list); 82 block->net = net; 83 block->sw = sw; 84 85 return block; 86 } 87 88 static void prestera_flow_block_release(void *cb_priv) 89 { 90 struct prestera_flow_block *block = cb_priv; 91 92 prestera_flow_block_destroy(block); 93 } 94 95 static bool 96 prestera_flow_block_is_bound(const struct prestera_flow_block *block) 97 { 98 return block->ruleset_zero; 99 } 100 101 static struct prestera_flow_block_binding * 102 prestera_flow_block_lookup(struct prestera_flow_block *block, 103 struct prestera_port *port) 104 { 105 struct prestera_flow_block_binding *binding; 106 107 list_for_each_entry(binding, &block->binding_list, list) 108 if (binding->port == port) 109 return binding; 110 111 return NULL; 112 } 113 114 static int prestera_flow_block_bind(struct prestera_flow_block *block, 115 struct prestera_port *port) 116 { 117 struct prestera_flow_block_binding *binding; 118 int err; 119 120 binding = kzalloc(sizeof(*binding), GFP_KERNEL); 121 if (!binding) 122 return -ENOMEM; 123 124 binding->span_id = PRESTERA_SPAN_INVALID_ID; 125 binding->port = port; 126 127 if (prestera_flow_block_is_bound(block)) { 128 err = prestera_acl_ruleset_bind(block->ruleset_zero, port); 129 if (err) 130 goto err_ruleset_bind; 131 } 132 133 list_add(&binding->list, &block->binding_list); 134 return 0; 135 136 err_ruleset_bind: 137 kfree(binding); 138 return err; 139 } 140 141 static int prestera_flow_block_unbind(struct prestera_flow_block *block, 142 struct prestera_port *port) 143 { 144 struct prestera_flow_block_binding *binding; 145 146 binding = prestera_flow_block_lookup(block, port); 147 if (!binding) 148 return -ENOENT; 149 150 list_del(&binding->list); 151 152 if (prestera_flow_block_is_bound(block)) 153 prestera_acl_ruleset_unbind(block->ruleset_zero, port); 154 155 kfree(binding); 156 return 0; 157 } 158 159 static struct prestera_flow_block * 160 prestera_flow_block_get(struct prestera_switch *sw, 161 struct flow_block_offload *f, 162 bool *register_block) 163 { 164 struct prestera_flow_block *block; 165 struct flow_block_cb *block_cb; 166 167 block_cb = flow_block_cb_lookup(f->block, 168 prestera_flow_block_cb, sw); 169 if (!block_cb) { 170 block = prestera_flow_block_create(sw, f->net); 171 if (!block) 172 return ERR_PTR(-ENOMEM); 173 174 block_cb = flow_block_cb_alloc(prestera_flow_block_cb, 175 sw, block, 176 prestera_flow_block_release); 177 if (IS_ERR(block_cb)) { 178 prestera_flow_block_destroy(block); 179 return ERR_CAST(block_cb); 180 } 181 182 block->block_cb = block_cb; 183 *register_block = true; 184 } else { 185 block = flow_block_cb_priv(block_cb); 186 *register_block = false; 187 } 188 189 flow_block_cb_incref(block_cb); 190 191 return block; 192 } 193 194 static void prestera_flow_block_put(struct prestera_flow_block *block) 195 { 196 struct flow_block_cb *block_cb = block->block_cb; 197 198 if (flow_block_cb_decref(block_cb)) 199 return; 200 201 flow_block_cb_free(block_cb); 202 prestera_flow_block_destroy(block); 203 } 204 205 static int prestera_setup_flow_block_bind(struct prestera_port *port, 206 struct flow_block_offload *f) 207 { 208 struct prestera_switch *sw = port->sw; 209 struct prestera_flow_block *block; 210 struct flow_block_cb *block_cb; 211 bool register_block; 212 int err; 213 214 block = prestera_flow_block_get(sw, f, ®ister_block); 215 if (IS_ERR(block)) 216 return PTR_ERR(block); 217 218 block_cb = block->block_cb; 219 220 err = prestera_flow_block_bind(block, port); 221 if (err) 222 goto err_block_bind; 223 224 if (register_block) { 225 flow_block_cb_add(block_cb, f); 226 list_add_tail(&block_cb->driver_list, &prestera_block_cb_list); 227 } 228 229 port->flow_block = block; 230 return 0; 231 232 err_block_bind: 233 prestera_flow_block_put(block); 234 235 return err; 236 } 237 238 static void prestera_setup_flow_block_unbind(struct prestera_port *port, 239 struct flow_block_offload *f) 240 { 241 struct prestera_switch *sw = port->sw; 242 struct prestera_flow_block *block; 243 struct flow_block_cb *block_cb; 244 int err; 245 246 block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw); 247 if (!block_cb) 248 return; 249 250 block = flow_block_cb_priv(block_cb); 251 252 prestera_span_destroy(block); 253 254 err = prestera_flow_block_unbind(block, port); 255 if (err) 256 goto error; 257 258 if (!flow_block_cb_decref(block_cb)) { 259 flow_block_cb_remove(block_cb, f); 260 list_del(&block_cb->driver_list); 261 } 262 error: 263 port->flow_block = NULL; 264 } 265 266 int prestera_flow_block_setup(struct prestera_port *port, 267 struct flow_block_offload *f) 268 { 269 if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 270 return -EOPNOTSUPP; 271 272 f->driver_block_list = &prestera_block_cb_list; 273 274 switch (f->command) { 275 case FLOW_BLOCK_BIND: 276 return prestera_setup_flow_block_bind(port, f); 277 case FLOW_BLOCK_UNBIND: 278 prestera_setup_flow_block_unbind(port, f); 279 return 0; 280 default: 281 return -EOPNOTSUPP; 282 } 283 } 284