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