Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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(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 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(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 */
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}