Merge tag 'sound-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[linux-block.git] / drivers / net / ethernet / sfc / tc_bindings.c
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(binding->efx, binding->otherdev,
44                                      tcf, 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(&binding->list);
55         kfree(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(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
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
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  */
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
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 }