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 
efx_tc_find_binding(struct efx_nic * efx,struct net_device * otherdev)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 
efx_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)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(binding->efx, binding->otherdev,
44 				     tcf, binding->efv);
45 	default:
46 		return -EOPNOTSUPP;
47 	}
48 }
49 
efx_tc_block_unbind(void * cb_priv)50 void 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 
efx_tc_create_binding(struct efx_nic * efx,struct efx_rep * efv,struct net_device * otherdev,struct flow_block * block)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(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 
efx_tc_setup_block(struct net_device * net_dev,struct efx_nic * efx,struct flow_block_offload * tcb,struct efx_rep * efv)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, 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 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 
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))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(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 implementation
211  * Entry point for flower block and filter management.
212  */
efx_tc_setup(struct net_device * net_dev,enum tc_setup_type type,void * type_data)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(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 
efx_tc_netdev_event(struct efx_nic * efx,unsigned long event,struct net_device * net_dev)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