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_release(void *cb_priv) 64 { 65 struct prestera_flow_block *block = cb_priv; 66 67 prestera_acl_block_destroy(block); 68 } 69 70 static struct prestera_flow_block * 71 prestera_flow_block_get(struct prestera_switch *sw, 72 struct flow_block_offload *f, 73 bool *register_block) 74 { 75 struct prestera_flow_block *block; 76 struct flow_block_cb *block_cb; 77 78 block_cb = flow_block_cb_lookup(f->block, 79 prestera_flow_block_cb, sw); 80 if (!block_cb) { 81 block = prestera_acl_block_create(sw, f->net); 82 if (!block) 83 return ERR_PTR(-ENOMEM); 84 85 block_cb = flow_block_cb_alloc(prestera_flow_block_cb, 86 sw, block, 87 prestera_flow_block_release); 88 if (IS_ERR(block_cb)) { 89 prestera_acl_block_destroy(block); 90 return ERR_CAST(block_cb); 91 } 92 93 block->block_cb = block_cb; 94 *register_block = true; 95 } else { 96 block = flow_block_cb_priv(block_cb); 97 *register_block = false; 98 } 99 100 flow_block_cb_incref(block_cb); 101 102 return block; 103 } 104 105 static void prestera_flow_block_put(struct prestera_flow_block *block) 106 { 107 struct flow_block_cb *block_cb = block->block_cb; 108 109 if (flow_block_cb_decref(block_cb)) 110 return; 111 112 flow_block_cb_free(block_cb); 113 prestera_acl_block_destroy(block); 114 } 115 116 static int prestera_setup_flow_block_bind(struct prestera_port *port, 117 struct flow_block_offload *f) 118 { 119 struct prestera_switch *sw = port->sw; 120 struct prestera_flow_block *block; 121 struct flow_block_cb *block_cb; 122 bool register_block; 123 int err; 124 125 block = prestera_flow_block_get(sw, f, ®ister_block); 126 if (IS_ERR(block)) 127 return PTR_ERR(block); 128 129 block_cb = block->block_cb; 130 131 err = prestera_acl_block_bind(block, port); 132 if (err) 133 goto err_block_bind; 134 135 if (register_block) { 136 flow_block_cb_add(block_cb, f); 137 list_add_tail(&block_cb->driver_list, &prestera_block_cb_list); 138 } 139 140 port->flow_block = block; 141 return 0; 142 143 err_block_bind: 144 prestera_flow_block_put(block); 145 146 return err; 147 } 148 149 static void prestera_setup_flow_block_unbind(struct prestera_port *port, 150 struct flow_block_offload *f) 151 { 152 struct prestera_switch *sw = port->sw; 153 struct prestera_flow_block *block; 154 struct flow_block_cb *block_cb; 155 int err; 156 157 block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw); 158 if (!block_cb) 159 return; 160 161 block = flow_block_cb_priv(block_cb); 162 163 prestera_span_destroy(block); 164 165 err = prestera_acl_block_unbind(block, port); 166 if (err) 167 goto error; 168 169 if (!flow_block_cb_decref(block_cb)) { 170 flow_block_cb_remove(block_cb, f); 171 list_del(&block_cb->driver_list); 172 } 173 error: 174 port->flow_block = NULL; 175 } 176 177 int prestera_flow_block_setup(struct prestera_port *port, 178 struct flow_block_offload *f) 179 { 180 if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 181 return -EOPNOTSUPP; 182 183 f->driver_block_list = &prestera_block_cb_list; 184 185 switch (f->command) { 186 case FLOW_BLOCK_BIND: 187 return prestera_setup_flow_block_bind(port, f); 188 case FLOW_BLOCK_UNBIND: 189 prestera_setup_flow_block_unbind(port, f); 190 return 0; 191 default: 192 return -EOPNOTSUPP; 193 } 194 } 195