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 | |
15 | struct 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 | |
23 | static 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 | |
35 | static 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 | |
50 | void 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 | |
58 | static 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 | |
74 | int 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 | |
139 | int 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 | */ |
213 | int 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 | |
231 | int 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 | |