242 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/****************************************************************************3 * Driver for Solarflare network controllers and boards4 * Copyright 2022 Xilinx Inc.5 *6 * This program is free software; you can redistribute it and/or modify it7 * under the terms of the GNU General Public License version 2 as published8 * by the Free Software Foundation, incorporated herein by reference.9 */10 11#include "tc_bindings.h"12#include "tc.h"13#include "tc_encap_actions.h"14 15struct efx_tc_block_binding {16 struct list_head list;17 struct efx_nic *efx;18 struct efx_rep *efv;19 struct net_device *otherdev; /* may actually be us */20 struct flow_block *block;21};22 23static struct efx_tc_block_binding *efx_tc_find_binding(struct efx_nic *efx,24 struct net_device *otherdev)25{26 struct efx_tc_block_binding *binding;27 28 ASSERT_RTNL();29 list_for_each_entry(binding, &efx->tc->block_list, list)30 if (binding->otherdev == otherdev)31 return binding;32 return NULL;33}34 35static int efx_tc_block_cb(enum tc_setup_type type, void *type_data,36 void *cb_priv)37{38 struct efx_tc_block_binding *binding = cb_priv;39 struct flow_cls_offload *tcf = type_data;40 41 switch (type) {42 case TC_SETUP_CLSFLOWER:43 return efx_tc_flower(binding->efx, binding->otherdev,44 tcf, binding->efv);45 default:46 return -EOPNOTSUPP;47 }48}49 50void efx_tc_block_unbind(void *cb_priv)51{52 struct efx_tc_block_binding *binding = cb_priv;53 54 list_del(&binding->list);55 kfree(binding);56}57 58static struct efx_tc_block_binding *efx_tc_create_binding(59 struct efx_nic *efx, struct efx_rep *efv,60 struct net_device *otherdev, struct flow_block *block)61{62 struct efx_tc_block_binding *binding = kmalloc(sizeof(*binding), GFP_KERNEL);63 64 if (!binding)65 return ERR_PTR(-ENOMEM);66 binding->efx = efx;67 binding->efv = efv;68 binding->otherdev = otherdev;69 binding->block = block;70 list_add(&binding->list, &efx->tc->block_list);71 return binding;72}73 74int efx_tc_setup_block(struct net_device *net_dev, struct efx_nic *efx,75 struct flow_block_offload *tcb, struct efx_rep *efv)76{77 struct efx_tc_block_binding *binding;78 struct flow_block_cb *block_cb;79 int rc;80 81 if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)82 return -EOPNOTSUPP;83 84 if (WARN_ON(!efx->tc))85 return -ENETDOWN;86 87 switch (tcb->command) {88 case FLOW_BLOCK_BIND:89 binding = efx_tc_create_binding(efx, efv, net_dev, tcb->block);90 if (IS_ERR(binding))91 return PTR_ERR(binding);92 block_cb = flow_block_cb_alloc(efx_tc_block_cb, binding,93 binding, efx_tc_block_unbind);94 rc = PTR_ERR_OR_ZERO(block_cb);95 netif_dbg(efx, drv, efx->net_dev,96 "bind %sdirect block for device %s, rc %d\n",97 net_dev == efx->net_dev ? "" :98 efv ? "semi" : "in",99 net_dev ? net_dev->name : NULL, rc);100 if (rc) {101 list_del(&binding->list);102 kfree(binding);103 } else {104 flow_block_cb_add(block_cb, tcb);105 }106 return rc;107 case FLOW_BLOCK_UNBIND:108 binding = efx_tc_find_binding(efx, net_dev);109 if (binding) {110 block_cb = flow_block_cb_lookup(tcb->block,111 efx_tc_block_cb,112 binding);113 if (block_cb) {114 flow_block_cb_remove(block_cb, tcb);115 netif_dbg(efx, drv, efx->net_dev,116 "unbound %sdirect block for device %s\n",117 net_dev == efx->net_dev ? "" :118 binding->efv ? "semi" : "in",119 net_dev ? net_dev->name : NULL);120 return 0;121 }122 }123 /* If we're in driver teardown, then we expect to have124 * already unbound all our blocks (we did it early while125 * we still had MCDI to remove the filters), so getting126 * unbind callbacks now isn't a problem.127 */128 netif_cond_dbg(efx, drv, efx->net_dev,129 !efx->tc->up, warn,130 "%sdirect block unbind for device %s, was never bound\n",131 net_dev == efx->net_dev ? "" : "in",132 net_dev ? net_dev->name : NULL);133 return -ENOENT;134 default:135 return -EOPNOTSUPP;136 }137}138 139int efx_tc_indr_setup_cb(struct net_device *net_dev, struct Qdisc *sch,140 void *cb_priv, enum tc_setup_type type,141 void *type_data, void *data,142 void (*cleanup)(struct flow_block_cb *block_cb))143{144 struct flow_block_offload *tcb = type_data;145 struct efx_tc_block_binding *binding;146 struct flow_block_cb *block_cb;147 struct efx_nic *efx = cb_priv;148 bool is_ovs_int_port;149 int rc;150 151 if (!net_dev)152 return -EOPNOTSUPP;153 154 if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&155 tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)156 return -EOPNOTSUPP;157 158 is_ovs_int_port = netif_is_ovs_master(net_dev);159 if (tcb->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS &&160 !is_ovs_int_port)161 return -EOPNOTSUPP;162 163 if (is_ovs_int_port)164 return -EOPNOTSUPP;165 166 switch (type) {167 case TC_SETUP_BLOCK:168 switch (tcb->command) {169 case FLOW_BLOCK_BIND:170 binding = efx_tc_create_binding(efx, NULL, net_dev, tcb->block);171 if (IS_ERR(binding))172 return PTR_ERR(binding);173 block_cb = flow_indr_block_cb_alloc(efx_tc_block_cb, binding,174 binding, efx_tc_block_unbind,175 tcb, net_dev, sch, data, binding,176 cleanup);177 rc = PTR_ERR_OR_ZERO(block_cb);178 netif_dbg(efx, drv, efx->net_dev,179 "bind indr block for device %s, rc %d\n",180 net_dev ? net_dev->name : NULL, rc);181 if (rc) {182 list_del(&binding->list);183 kfree(binding);184 } else {185 flow_block_cb_add(block_cb, tcb);186 }187 return rc;188 case FLOW_BLOCK_UNBIND:189 binding = efx_tc_find_binding(efx, net_dev);190 if (!binding)191 return -ENOENT;192 block_cb = flow_block_cb_lookup(tcb->block,193 efx_tc_block_cb,194 binding);195 if (!block_cb)196 return -ENOENT;197 flow_indr_block_cb_remove(block_cb, tcb);198 netif_dbg(efx, drv, efx->net_dev,199 "unbind indr block for device %s\n",200 net_dev ? net_dev->name : NULL);201 return 0;202 default:203 return -EOPNOTSUPP;204 }205 default:206 return -EOPNOTSUPP;207 }208}209 210/* .ndo_setup_tc implementation211 * Entry point for flower block and filter management.212 */213int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,214 void *type_data)215{216 struct efx_nic *efx = efx_netdev_priv(net_dev);217 218 if (efx->type->is_vf)219 return -EOPNOTSUPP;220 if (!efx->tc)221 return -EOPNOTSUPP;222 223 if (type == TC_SETUP_CLSFLOWER)224 return efx_tc_flower(efx, net_dev, type_data, NULL);225 if (type == TC_SETUP_BLOCK)226 return efx_tc_setup_block(net_dev, efx, type_data, NULL);227 228 return -EOPNOTSUPP;229}230 231int efx_tc_netdev_event(struct efx_nic *efx, unsigned long event,232 struct net_device *net_dev)233{234 if (efx->type->is_vf)235 return NOTIFY_DONE;236 237 if (event == NETDEV_UNREGISTER)238 efx_tc_unregister_egdev(efx, net_dev);239 240 return NOTIFY_OK;241}242