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 case FLOW_CLS_TMPLT_CREATE: 44 return prestera_flower_tmplt_create(block, f); 45 case FLOW_CLS_TMPLT_DESTROY: 46 prestera_flower_tmplt_destroy(block, f); 47 return 0; 48 default: 49 return -EOPNOTSUPP; 50 } 51 } 52 53 static int prestera_flow_block_cb(enum tc_setup_type type, 54 void *type_data, void *cb_priv) 55 { 56 struct prestera_flow_block *block = cb_priv; 57 58 switch (type) { 59 case TC_SETUP_CLSFLOWER: 60 return prestera_flow_block_flower_cb(block, type_data); 61 case TC_SETUP_CLSMATCHALL: 62 return prestera_flow_block_mall_cb(block, type_data); 63 default: 64 return -EOPNOTSUPP; 65 } 66 } 67 68 static void prestera_flow_block_destroy(void *cb_priv) 69 { 70 struct prestera_flow_block *block = cb_priv; 71 72 prestera_flower_template_cleanup(block); 73 74 WARN_ON(!list_empty(&block->binding_list)); 75 76 kfree(block); 77 } 78 79 static struct prestera_flow_block * 80 prestera_flow_block_create(struct prestera_switch *sw, struct net *net) 81 { 82 struct prestera_flow_block *block; 83 84 block = kzalloc(sizeof(*block), GFP_KERNEL); 85 if (!block) 86 return NULL; 87 88 INIT_LIST_HEAD(&block->binding_list); 89 block->net = net; 90 block->sw = sw; 91 92 return block; 93 } 94 95 static void prestera_flow_block_release(void *cb_priv) 96 { 97 struct prestera_flow_block *block = cb_priv; 98 99 prestera_flow_block_destroy(block); 100 } 101 102 static bool 103 prestera_flow_block_is_bound(const struct prestera_flow_block *block) 104 { 105 return block->ruleset_zero; 106 } 107 108 static struct prestera_flow_block_binding * 109 prestera_flow_block_lookup(struct prestera_flow_block *block, 110 struct prestera_port *port) 111 { 112 struct prestera_flow_block_binding *binding; 113 114 list_for_each_entry(binding, &block->binding_list, list) 115 if (binding->port == port) 116 return binding; 117 118 return NULL; 119 } 120 121 static int prestera_flow_block_bind(struct prestera_flow_block *block, 122 struct prestera_port *port) 123 { 124 struct prestera_flow_block_binding *binding; 125 int err; 126 127 binding = kzalloc(sizeof(*binding), GFP_KERNEL); 128 if (!binding) 129 return -ENOMEM; 130 131 binding->span_id = PRESTERA_SPAN_INVALID_ID; 132 binding->port = port; 133 134 if (prestera_flow_block_is_bound(block)) { 135 err = prestera_acl_ruleset_bind(block->ruleset_zero, port); 136 if (err) 137 goto err_ruleset_bind; 138 } 139 140 list_add(&binding->list, &block->binding_list); 141 return 0; 142 143 err_ruleset_bind: 144 kfree(binding); 145 return err; 146 } 147 148 static int prestera_flow_block_unbind(struct prestera_flow_block *block, 149 struct prestera_port *port) 150 { 151 struct prestera_flow_block_binding *binding; 152 153 binding = prestera_flow_block_lookup(block, port); 154 if (!binding) 155 return -ENOENT; 156 157 list_del(&binding->list); 158 159 if (prestera_flow_block_is_bound(block)) 160 prestera_acl_ruleset_unbind(block->ruleset_zero, port); 161 162 kfree(binding); 163 return 0; 164 } 165 166 static struct prestera_flow_block * 167 prestera_flow_block_get(struct prestera_switch *sw, 168 struct flow_block_offload *f, 169 bool *register_block) 170 { 171 struct prestera_flow_block *block; 172 struct flow_block_cb *block_cb; 173 174 block_cb = flow_block_cb_lookup(f->block, 175 prestera_flow_block_cb, sw); 176 if (!block_cb) { 177 block = prestera_flow_block_create(sw, f->net); 178 if (!block) 179 return ERR_PTR(-ENOMEM); 180 181 block_cb = flow_block_cb_alloc(prestera_flow_block_cb, 182 sw, block, 183 prestera_flow_block_release); 184 if (IS_ERR(block_cb)) { 185 prestera_flow_block_destroy(block); 186 return ERR_CAST(block_cb); 187 } 188 189 block->block_cb = block_cb; 190 *register_block = true; 191 } else { 192 block = flow_block_cb_priv(block_cb); 193 *register_block = false; 194 } 195 196 flow_block_cb_incref(block_cb); 197 198 return block; 199 } 200 201 static void prestera_flow_block_put(struct prestera_flow_block *block) 202 { 203 struct flow_block_cb *block_cb = block->block_cb; 204 205 if (flow_block_cb_decref(block_cb)) 206 return; 207 208 flow_block_cb_free(block_cb); 209 prestera_flow_block_destroy(block); 210 } 211 212 static int prestera_setup_flow_block_bind(struct prestera_port *port, 213 struct flow_block_offload *f) 214 { 215 struct prestera_switch *sw = port->sw; 216 struct prestera_flow_block *block; 217 struct flow_block_cb *block_cb; 218 bool register_block; 219 int err; 220 221 block = prestera_flow_block_get(sw, f, ®ister_block); 222 if (IS_ERR(block)) 223 return PTR_ERR(block); 224 225 block_cb = block->block_cb; 226 227 err = prestera_flow_block_bind(block, port); 228 if (err) 229 goto err_block_bind; 230 231 if (register_block) { 232 flow_block_cb_add(block_cb, f); 233 list_add_tail(&block_cb->driver_list, &prestera_block_cb_list); 234 } 235 236 port->flow_block = block; 237 return 0; 238 239 err_block_bind: 240 prestera_flow_block_put(block); 241 242 return err; 243 } 244 245 static void prestera_setup_flow_block_unbind(struct prestera_port *port, 246 struct flow_block_offload *f) 247 { 248 struct prestera_switch *sw = port->sw; 249 struct prestera_flow_block *block; 250 struct flow_block_cb *block_cb; 251 int err; 252 253 block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw); 254 if (!block_cb) 255 return; 256 257 block = flow_block_cb_priv(block_cb); 258 259 prestera_span_destroy(block); 260 261 err = prestera_flow_block_unbind(block, port); 262 if (err) 263 goto error; 264 265 if (!flow_block_cb_decref(block_cb)) { 266 flow_block_cb_remove(block_cb, f); 267 list_del(&block_cb->driver_list); 268 } 269 error: 270 port->flow_block = NULL; 271 } 272 273 int prestera_flow_block_setup(struct prestera_port *port, 274 struct flow_block_offload *f) 275 { 276 if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 277 return -EOPNOTSUPP; 278 279 f->driver_block_list = &prestera_block_cb_list; 280 281 switch (f->command) { 282 case FLOW_BLOCK_BIND: 283 return prestera_setup_flow_block_bind(port, f); 284 case FLOW_BLOCK_UNBIND: 285 prestera_setup_flow_block_unbind(port, f); 286 return 0; 287 default: 288 return -EOPNOTSUPP; 289 } 290 } 291