1// SPDX-License-Identifier: GPL-2.0-only
2/****************************************************************************
3 * Driver for Solarflare network controllers and boards
4 * Copyright 2022 Xilinx Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * 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(efx: binding->efx, net_dev: binding->otherdev,
44 tc: tcf, efv: 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(entry: &binding->list);
55 kfree(objp: 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(size: sizeof(*binding), GFP_KERNEL);
63
64 if (!binding)
65 return ERR_PTR(error: -ENOMEM);
66 binding->efx = efx;
67 binding->efv = efv;
68 binding->otherdev = otherdev;
69 binding->block = block;
70 list_add(new: &binding->list, head: &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, otherdev: net_dev, block: tcb->block);
90 if (IS_ERR(ptr: binding))
91 return PTR_ERR(ptr: binding);
92 block_cb = flow_block_cb_alloc(cb: efx_tc_block_cb, cb_ident: binding,
93 cb_priv: binding, release: efx_tc_block_unbind);
94 rc = PTR_ERR_OR_ZERO(ptr: 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(entry: &binding->list);
102 kfree(objp: binding);
103 } else {
104 flow_block_cb_add(block_cb, offload: tcb);
105 }
106 return rc;
107 case FLOW_BLOCK_UNBIND:
108 binding = efx_tc_find_binding(efx, otherdev: net_dev);
109 if (binding) {
110 block_cb = flow_block_cb_lookup(block: tcb->block,
111 cb: efx_tc_block_cb,
112 cb_ident: binding);
113 if (block_cb) {
114 flow_block_cb_remove(block_cb, offload: 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 have
124 * already unbound all our blocks (we did it early while
125 * we still had MCDI to remove the filters), so getting
126 * 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(dev: 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, otherdev: net_dev, block: tcb->block);
171 if (IS_ERR(ptr: binding))
172 return PTR_ERR(ptr: binding);
173 block_cb = flow_indr_block_cb_alloc(cb: efx_tc_block_cb, cb_ident: binding,
174 cb_priv: binding, release: efx_tc_block_unbind,
175 bo: tcb, dev: net_dev, sch, data, indr_cb_priv: binding,
176 cleanup);
177 rc = PTR_ERR_OR_ZERO(ptr: 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(entry: &binding->list);
183 kfree(objp: binding);
184 } else {
185 flow_block_cb_add(block_cb, offload: tcb);
186 }
187 return rc;
188 case FLOW_BLOCK_UNBIND:
189 binding = efx_tc_find_binding(efx, otherdev: net_dev);
190 if (!binding)
191 return -ENOENT;
192 block_cb = flow_block_cb_lookup(block: tcb->block,
193 cb: efx_tc_block_cb,
194 cb_ident: binding);
195 if (!block_cb)
196 return -ENOENT;
197 flow_indr_block_cb_remove(block_cb, offload: 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 implementation
211 * 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(dev: 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, tc: type_data, NULL);
225 if (type == TC_SETUP_BLOCK)
226 return efx_tc_setup_block(net_dev, efx, tcb: 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

source code of linux/drivers/net/ethernet/sfc/tc_bindings.c