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
14 struct efx_tc_block_binding {
15 struct list_head list;
16 struct efx_nic *efx;
17 struct efx_rep *efv;
18 struct net_device *otherdev; /* may actually be us */
19 struct flow_block *block;
20 };
21
efx_tc_find_binding(struct efx_nic * efx,struct net_device * otherdev)22 static struct efx_tc_block_binding *efx_tc_find_binding(struct efx_nic *efx,
23 struct net_device *otherdev)
24 {
25 struct efx_tc_block_binding *binding;
26
27 ASSERT_RTNL();
28 list_for_each_entry(binding, &efx->tc->block_list, list)
29 if (binding->otherdev == otherdev)
30 return binding;
31 return NULL;
32 }
33
efx_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)34 static int efx_tc_block_cb(enum tc_setup_type type, void *type_data,
35 void *cb_priv)
36 {
37 struct efx_tc_block_binding *binding = cb_priv;
38 struct flow_cls_offload *tcf = type_data;
39
40 switch (type) {
41 case TC_SETUP_CLSFLOWER:
42 return efx_tc_flower(binding->efx, binding->otherdev,
43 tcf, binding->efv);
44 default:
45 return -EOPNOTSUPP;
46 }
47 }
48
efx_tc_block_unbind(void * cb_priv)49 void efx_tc_block_unbind(void *cb_priv)
50 {
51 struct efx_tc_block_binding *binding = cb_priv;
52
53 list_del(&binding->list);
54 kfree(binding);
55 }
56
efx_tc_create_binding(struct efx_nic * efx,struct efx_rep * efv,struct net_device * otherdev,struct flow_block * block)57 static struct efx_tc_block_binding *efx_tc_create_binding(
58 struct efx_nic *efx, struct efx_rep *efv,
59 struct net_device *otherdev, struct flow_block *block)
60 {
61 struct efx_tc_block_binding *binding = kmalloc(sizeof(*binding), GFP_KERNEL);
62
63 if (!binding)
64 return ERR_PTR(-ENOMEM);
65 binding->efx = efx;
66 binding->efv = efv;
67 binding->otherdev = otherdev;
68 binding->block = block;
69 list_add(&binding->list, &efx->tc->block_list);
70 return binding;
71 }
72
efx_tc_setup_block(struct net_device * net_dev,struct efx_nic * efx,struct flow_block_offload * tcb,struct efx_rep * efv)73 int efx_tc_setup_block(struct net_device *net_dev, struct efx_nic *efx,
74 struct flow_block_offload *tcb, struct efx_rep *efv)
75 {
76 struct efx_tc_block_binding *binding;
77 struct flow_block_cb *block_cb;
78 int rc;
79
80 if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
81 return -EOPNOTSUPP;
82
83 if (WARN_ON(!efx->tc))
84 return -ENETDOWN;
85
86 switch (tcb->command) {
87 case FLOW_BLOCK_BIND:
88 binding = efx_tc_create_binding(efx, efv, net_dev, tcb->block);
89 if (IS_ERR(binding))
90 return PTR_ERR(binding);
91 block_cb = flow_block_cb_alloc(efx_tc_block_cb, binding,
92 binding, efx_tc_block_unbind);
93 rc = PTR_ERR_OR_ZERO(block_cb);
94 netif_dbg(efx, drv, efx->net_dev,
95 "bind %sdirect block for device %s, rc %d\n",
96 net_dev == efx->net_dev ? "" :
97 efv ? "semi" : "in",
98 net_dev ? net_dev->name : NULL, rc);
99 if (rc) {
100 list_del(&binding->list);
101 kfree(binding);
102 } else {
103 flow_block_cb_add(block_cb, tcb);
104 }
105 return rc;
106 case FLOW_BLOCK_UNBIND:
107 binding = efx_tc_find_binding(efx, net_dev);
108 if (binding) {
109 block_cb = flow_block_cb_lookup(tcb->block,
110 efx_tc_block_cb,
111 binding);
112 if (block_cb) {
113 flow_block_cb_remove(block_cb, tcb);
114 netif_dbg(efx, drv, efx->net_dev,
115 "unbound %sdirect block for device %s\n",
116 net_dev == efx->net_dev ? "" :
117 binding->efv ? "semi" : "in",
118 net_dev ? net_dev->name : NULL);
119 return 0;
120 }
121 }
122 /* If we're in driver teardown, then we expect to have
123 * already unbound all our blocks (we did it early while
124 * we still had MCDI to remove the filters), so getting
125 * unbind callbacks now isn't a problem.
126 */
127 netif_cond_dbg(efx, drv, efx->net_dev,
128 !efx->tc->up, warn,
129 "%sdirect block unbind for device %s, was never bound\n",
130 net_dev == efx->net_dev ? "" : "in",
131 net_dev ? net_dev->name : NULL);
132 return -ENOENT;
133 default:
134 return -EOPNOTSUPP;
135 }
136 }
137
efx_tc_indr_setup_cb(struct net_device * net_dev,struct Qdisc * sch,void * cb_priv,enum tc_setup_type type,void * type_data,void * data,void (* cleanup)(struct flow_block_cb * block_cb))138 int efx_tc_indr_setup_cb(struct net_device *net_dev, struct Qdisc *sch,
139 void *cb_priv, enum tc_setup_type type,
140 void *type_data, void *data,
141 void (*cleanup)(struct flow_block_cb *block_cb))
142 {
143 struct flow_block_offload *tcb = type_data;
144 struct efx_tc_block_binding *binding;
145 struct flow_block_cb *block_cb;
146 struct efx_nic *efx = cb_priv;
147 bool is_ovs_int_port;
148 int rc;
149
150 if (!net_dev)
151 return -EOPNOTSUPP;
152
153 if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
154 tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
155 return -EOPNOTSUPP;
156
157 is_ovs_int_port = netif_is_ovs_master(net_dev);
158 if (tcb->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS &&
159 !is_ovs_int_port)
160 return -EOPNOTSUPP;
161
162 if (is_ovs_int_port)
163 return -EOPNOTSUPP;
164
165 switch (type) {
166 case TC_SETUP_BLOCK:
167 switch (tcb->command) {
168 case FLOW_BLOCK_BIND:
169 binding = efx_tc_create_binding(efx, NULL, net_dev, tcb->block);
170 if (IS_ERR(binding))
171 return PTR_ERR(binding);
172 block_cb = flow_indr_block_cb_alloc(efx_tc_block_cb, binding,
173 binding, efx_tc_block_unbind,
174 tcb, net_dev, sch, data, binding,
175 cleanup);
176 rc = PTR_ERR_OR_ZERO(block_cb);
177 netif_dbg(efx, drv, efx->net_dev,
178 "bind indr block for device %s, rc %d\n",
179 net_dev ? net_dev->name : NULL, rc);
180 if (rc) {
181 list_del(&binding->list);
182 kfree(binding);
183 } else {
184 flow_block_cb_add(block_cb, tcb);
185 }
186 return rc;
187 case FLOW_BLOCK_UNBIND:
188 binding = efx_tc_find_binding(efx, net_dev);
189 if (!binding)
190 return -ENOENT;
191 block_cb = flow_block_cb_lookup(tcb->block,
192 efx_tc_block_cb,
193 binding);
194 if (!block_cb)
195 return -ENOENT;
196 flow_indr_block_cb_remove(block_cb, tcb);
197 netif_dbg(efx, drv, efx->net_dev,
198 "unbind indr block for device %s\n",
199 net_dev ? net_dev->name : NULL);
200 return 0;
201 default:
202 return -EOPNOTSUPP;
203 }
204 default:
205 return -EOPNOTSUPP;
206 }
207 }
208
209 /* .ndo_setup_tc implementation
210 * Entry point for flower block and filter management.
211 */
efx_tc_setup(struct net_device * net_dev,enum tc_setup_type type,void * type_data)212 int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,
213 void *type_data)
214 {
215 struct efx_nic *efx = efx_netdev_priv(net_dev);
216
217 if (efx->type->is_vf)
218 return -EOPNOTSUPP;
219 if (!efx->tc)
220 return -EOPNOTSUPP;
221
222 if (type == TC_SETUP_CLSFLOWER)
223 return efx_tc_flower(efx, net_dev, type_data, NULL);
224 if (type == TC_SETUP_BLOCK)
225 return efx_tc_setup_block(net_dev, efx, type_data, NULL);
226
227 return -EOPNOTSUPP;
228 }
229