0001
0002
0003
0004 #include <linux/kernel.h>
0005 #include <linux/slab.h>
0006 #include <linux/errno.h>
0007 #include <linux/list.h>
0008 #include <net/net_namespace.h>
0009
0010 #include "spectrum.h"
0011
0012 struct mlxsw_sp_flow_block *
0013 mlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp, struct net *net)
0014 {
0015 struct mlxsw_sp_flow_block *block;
0016
0017 block = kzalloc(sizeof(*block), GFP_KERNEL);
0018 if (!block)
0019 return NULL;
0020 INIT_LIST_HEAD(&block->binding_list);
0021 INIT_LIST_HEAD(&block->mall.list);
0022 block->mlxsw_sp = mlxsw_sp;
0023 block->net = net;
0024 return block;
0025 }
0026
0027 void mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block)
0028 {
0029 WARN_ON(!list_empty(&block->binding_list));
0030 kfree(block);
0031 }
0032
0033 static struct mlxsw_sp_flow_block_binding *
0034 mlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block *block,
0035 struct mlxsw_sp_port *mlxsw_sp_port, bool ingress)
0036 {
0037 struct mlxsw_sp_flow_block_binding *binding;
0038
0039 list_for_each_entry(binding, &block->binding_list, list)
0040 if (binding->mlxsw_sp_port == mlxsw_sp_port &&
0041 binding->ingress == ingress)
0042 return binding;
0043 return NULL;
0044 }
0045
0046 static bool
0047 mlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block *block)
0048 {
0049 return block->ruleset_zero;
0050 }
0051
0052 static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp,
0053 struct mlxsw_sp_flow_block *block,
0054 struct mlxsw_sp_port *mlxsw_sp_port,
0055 bool ingress,
0056 struct netlink_ext_ack *extack)
0057 {
0058 struct mlxsw_sp_flow_block_binding *binding;
0059 int err;
0060
0061 if (WARN_ON(mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress)))
0062 return -EEXIST;
0063
0064 if (ingress && block->ingress_blocker_rule_count) {
0065 NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules");
0066 return -EOPNOTSUPP;
0067 }
0068
0069 if (!ingress && block->egress_blocker_rule_count) {
0070 NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules");
0071 return -EOPNOTSUPP;
0072 }
0073
0074 err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port, extack);
0075 if (err)
0076 return err;
0077
0078 binding = kzalloc(sizeof(*binding), GFP_KERNEL);
0079 if (!binding) {
0080 err = -ENOMEM;
0081 goto err_binding_alloc;
0082 }
0083 binding->mlxsw_sp_port = mlxsw_sp_port;
0084 binding->ingress = ingress;
0085
0086 if (mlxsw_sp_flow_block_ruleset_bound(block)) {
0087 err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
0088 if (err)
0089 goto err_ruleset_bind;
0090 }
0091
0092 if (ingress)
0093 block->ingress_binding_count++;
0094 else
0095 block->egress_binding_count++;
0096 list_add(&binding->list, &block->binding_list);
0097 return 0;
0098
0099 err_ruleset_bind:
0100 kfree(binding);
0101 err_binding_alloc:
0102 mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
0103
0104 return err;
0105 }
0106
0107 static int mlxsw_sp_flow_block_unbind(struct mlxsw_sp *mlxsw_sp,
0108 struct mlxsw_sp_flow_block *block,
0109 struct mlxsw_sp_port *mlxsw_sp_port,
0110 bool ingress)
0111 {
0112 struct mlxsw_sp_flow_block_binding *binding;
0113
0114 binding = mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress);
0115 if (!binding)
0116 return -ENOENT;
0117
0118 list_del(&binding->list);
0119
0120 if (ingress)
0121 block->ingress_binding_count--;
0122 else
0123 block->egress_binding_count--;
0124
0125 if (mlxsw_sp_flow_block_ruleset_bound(block))
0126 mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
0127
0128 kfree(binding);
0129
0130 mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
0131
0132 return 0;
0133 }
0134
0135 static int mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block *flow_block,
0136 struct tc_cls_matchall_offload *f)
0137 {
0138 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
0139
0140 switch (f->command) {
0141 case TC_CLSMATCHALL_REPLACE:
0142 return mlxsw_sp_mall_replace(mlxsw_sp, flow_block, f);
0143 case TC_CLSMATCHALL_DESTROY:
0144 mlxsw_sp_mall_destroy(flow_block, f);
0145 return 0;
0146 default:
0147 return -EOPNOTSUPP;
0148 }
0149 }
0150
0151 static int mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block *flow_block,
0152 struct flow_cls_offload *f)
0153 {
0154 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
0155
0156 switch (f->command) {
0157 case FLOW_CLS_REPLACE:
0158 return mlxsw_sp_flower_replace(mlxsw_sp, flow_block, f);
0159 case FLOW_CLS_DESTROY:
0160 mlxsw_sp_flower_destroy(mlxsw_sp, flow_block, f);
0161 return 0;
0162 case FLOW_CLS_STATS:
0163 return mlxsw_sp_flower_stats(mlxsw_sp, flow_block, f);
0164 case FLOW_CLS_TMPLT_CREATE:
0165 return mlxsw_sp_flower_tmplt_create(mlxsw_sp, flow_block, f);
0166 case FLOW_CLS_TMPLT_DESTROY:
0167 mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, flow_block, f);
0168 return 0;
0169 default:
0170 return -EOPNOTSUPP;
0171 }
0172 }
0173
0174 static int mlxsw_sp_flow_block_cb(enum tc_setup_type type,
0175 void *type_data, void *cb_priv)
0176 {
0177 struct mlxsw_sp_flow_block *flow_block = cb_priv;
0178
0179 if (mlxsw_sp_flow_block_disabled(flow_block))
0180 return -EOPNOTSUPP;
0181
0182 switch (type) {
0183 case TC_SETUP_CLSMATCHALL:
0184 return mlxsw_sp_flow_block_mall_cb(flow_block, type_data);
0185 case TC_SETUP_CLSFLOWER:
0186 return mlxsw_sp_flow_block_flower_cb(flow_block, type_data);
0187 default:
0188 return -EOPNOTSUPP;
0189 }
0190 }
0191
0192 static void mlxsw_sp_tc_block_release(void *cb_priv)
0193 {
0194 struct mlxsw_sp_flow_block *flow_block = cb_priv;
0195
0196 mlxsw_sp_flow_block_destroy(flow_block);
0197 }
0198
0199 static LIST_HEAD(mlxsw_sp_block_cb_list);
0200
0201 static int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port,
0202 struct flow_block_offload *f,
0203 bool ingress)
0204 {
0205 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
0206 struct mlxsw_sp_flow_block *flow_block;
0207 struct flow_block_cb *block_cb;
0208 bool register_block = false;
0209 int err;
0210
0211 block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
0212 mlxsw_sp);
0213 if (!block_cb) {
0214 flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, f->net);
0215 if (!flow_block)
0216 return -ENOMEM;
0217 block_cb = flow_block_cb_alloc(mlxsw_sp_flow_block_cb,
0218 mlxsw_sp, flow_block,
0219 mlxsw_sp_tc_block_release);
0220 if (IS_ERR(block_cb)) {
0221 mlxsw_sp_flow_block_destroy(flow_block);
0222 return PTR_ERR(block_cb);
0223 }
0224 register_block = true;
0225 } else {
0226 flow_block = flow_block_cb_priv(block_cb);
0227 }
0228 flow_block_cb_incref(block_cb);
0229 err = mlxsw_sp_flow_block_bind(mlxsw_sp, flow_block,
0230 mlxsw_sp_port, ingress, f->extack);
0231 if (err)
0232 goto err_block_bind;
0233
0234 if (ingress)
0235 mlxsw_sp_port->ing_flow_block = flow_block;
0236 else
0237 mlxsw_sp_port->eg_flow_block = flow_block;
0238
0239 if (register_block) {
0240 flow_block_cb_add(block_cb, f);
0241 list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list);
0242 }
0243
0244 return 0;
0245
0246 err_block_bind:
0247 if (!flow_block_cb_decref(block_cb))
0248 flow_block_cb_free(block_cb);
0249 return err;
0250 }
0251
0252 static void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
0253 struct flow_block_offload *f,
0254 bool ingress)
0255 {
0256 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
0257 struct mlxsw_sp_flow_block *flow_block;
0258 struct flow_block_cb *block_cb;
0259 int err;
0260
0261 block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
0262 mlxsw_sp);
0263 if (!block_cb)
0264 return;
0265
0266 if (ingress)
0267 mlxsw_sp_port->ing_flow_block = NULL;
0268 else
0269 mlxsw_sp_port->eg_flow_block = NULL;
0270
0271 flow_block = flow_block_cb_priv(block_cb);
0272 err = mlxsw_sp_flow_block_unbind(mlxsw_sp, flow_block,
0273 mlxsw_sp_port, ingress);
0274 if (!err && !flow_block_cb_decref(block_cb)) {
0275 flow_block_cb_remove(block_cb, f);
0276 list_del(&block_cb->driver_list);
0277 }
0278 }
0279
0280 int mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port,
0281 struct flow_block_offload *f,
0282 bool ingress)
0283 {
0284 f->driver_block_list = &mlxsw_sp_block_cb_list;
0285
0286 switch (f->command) {
0287 case FLOW_BLOCK_BIND:
0288 return mlxsw_sp_setup_tc_block_bind(mlxsw_sp_port, f, ingress);
0289 case FLOW_BLOCK_UNBIND:
0290 mlxsw_sp_setup_tc_block_unbind(mlxsw_sp_port, f, ingress);
0291 return 0;
0292 default:
0293 return -EOPNOTSUPP;
0294 }
0295 }