xref: /openbmc/linux/drivers/net/ethernet/sfc/tc_bindings.c (revision 9dc0cad203ab57efac34e6bcb67635edf3b62ebf)
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 
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 
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 
49 static 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 
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 
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 
138 /* .ndo_setup_tc implementation
139  * Entry point for flower block and filter management.
140  */
141 int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,
142 		 void *type_data)
143 {
144 	struct efx_nic *efx = efx_netdev_priv(net_dev);
145 
146 	if (efx->type->is_vf)
147 		return -EOPNOTSUPP;
148 	if (!efx->tc)
149 		return -EOPNOTSUPP;
150 
151 	if (type == TC_SETUP_CLSFLOWER)
152 		return efx_tc_flower(efx, net_dev, type_data, NULL);
153 	if (type == TC_SETUP_BLOCK)
154 		return efx_tc_setup_block(net_dev, efx, type_data, NULL);
155 
156 	return -EOPNOTSUPP;
157 }
158