1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ |
3 | |
4 | #include <linux/bug.h> |
5 | #include <linux/lockdep.h> |
6 | #include <linux/rcupdate.h> |
7 | #include <linux/skbuff.h> |
8 | #include <linux/slab.h> |
9 | |
10 | #include "nfpcore/nfp_cpp.h" |
11 | #include "nfpcore/nfp_nffw.h" |
12 | #include "nfp_app.h" |
13 | #include "nfp_main.h" |
14 | #include "nfp_net.h" |
15 | #include "nfp_net_repr.h" |
16 | #include "nfp_port.h" |
17 | |
18 | static const struct nfp_app_type *apps[] = { |
19 | [NFP_APP_CORE_NIC] = &app_nic, |
20 | #ifdef CONFIG_BPF_SYSCALL |
21 | [NFP_APP_BPF_NIC] = &app_bpf, |
22 | #else |
23 | [NFP_APP_BPF_NIC] = &app_nic, |
24 | #endif |
25 | #ifdef CONFIG_NFP_APP_FLOWER |
26 | [NFP_APP_FLOWER_NIC] = &app_flower, |
27 | #endif |
28 | #ifdef CONFIG_NFP_APP_ABM_NIC |
29 | [NFP_APP_ACTIVE_BUFFER_MGMT_NIC] = &app_abm, |
30 | #endif |
31 | }; |
32 | |
33 | void nfp_check_rhashtable_empty(void *ptr, void *arg) |
34 | { |
35 | WARN_ON_ONCE(1); |
36 | } |
37 | |
38 | struct nfp_app *nfp_app_from_netdev(struct net_device *netdev) |
39 | { |
40 | if (nfp_netdev_is_nfp_net(netdev)) { |
41 | struct nfp_net *nn = netdev_priv(dev: netdev); |
42 | |
43 | return nn->app; |
44 | } |
45 | |
46 | if (nfp_netdev_is_nfp_repr(netdev)) { |
47 | struct nfp_repr *repr = netdev_priv(dev: netdev); |
48 | |
49 | return repr->app; |
50 | } |
51 | |
52 | WARN(1, "Unknown netdev type for nfp_app\n" ); |
53 | |
54 | return NULL; |
55 | } |
56 | |
57 | const char *nfp_app_mip_name(struct nfp_app *app) |
58 | { |
59 | if (!app || !app->pf->mip) |
60 | return "" ; |
61 | return nfp_mip_name(mip: app->pf->mip); |
62 | } |
63 | |
64 | int nfp_app_ndo_init(struct net_device *netdev) |
65 | { |
66 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
67 | |
68 | if (!app || !app->type->ndo_init) |
69 | return 0; |
70 | return app->type->ndo_init(app, netdev); |
71 | } |
72 | |
73 | void nfp_app_ndo_uninit(struct net_device *netdev) |
74 | { |
75 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
76 | |
77 | if (app && app->type->ndo_uninit) |
78 | app->type->ndo_uninit(app, netdev); |
79 | } |
80 | |
81 | u64 *nfp_app_port_get_stats(struct nfp_port *port, u64 *data) |
82 | { |
83 | if (!port || !port->app || !port->app->type->port_get_stats) |
84 | return data; |
85 | return port->app->type->port_get_stats(port->app, port, data); |
86 | } |
87 | |
88 | int nfp_app_port_get_stats_count(struct nfp_port *port) |
89 | { |
90 | if (!port || !port->app || !port->app->type->port_get_stats_count) |
91 | return 0; |
92 | return port->app->type->port_get_stats_count(port->app, port); |
93 | } |
94 | |
95 | u8 *nfp_app_port_get_stats_strings(struct nfp_port *port, u8 *data) |
96 | { |
97 | if (!port || !port->app || !port->app->type->port_get_stats_strings) |
98 | return data; |
99 | return port->app->type->port_get_stats_strings(port->app, port, data); |
100 | } |
101 | |
102 | struct sk_buff * |
103 | nfp_app_ctrl_msg_alloc(struct nfp_app *app, unsigned int size, gfp_t priority) |
104 | { |
105 | struct sk_buff *skb; |
106 | |
107 | if (nfp_app_ctrl_has_meta(app)) |
108 | size += 8; |
109 | |
110 | skb = alloc_skb(size, priority); |
111 | if (!skb) |
112 | return NULL; |
113 | |
114 | if (nfp_app_ctrl_has_meta(app)) |
115 | skb_reserve(skb, len: 8); |
116 | |
117 | return skb; |
118 | } |
119 | |
120 | struct nfp_reprs * |
121 | nfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type) |
122 | { |
123 | return rcu_dereference_protected(app->reprs[type], |
124 | nfp_app_is_locked(app)); |
125 | } |
126 | |
127 | struct nfp_reprs * |
128 | nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type, |
129 | struct nfp_reprs *reprs) |
130 | { |
131 | struct nfp_reprs *old; |
132 | |
133 | old = nfp_reprs_get_locked(app, type); |
134 | rtnl_lock(); |
135 | rcu_assign_pointer(app->reprs[type], reprs); |
136 | rtnl_unlock(); |
137 | |
138 | return old; |
139 | } |
140 | |
141 | static void |
142 | nfp_app_netdev_feat_change(struct nfp_app *app, struct net_device *netdev) |
143 | { |
144 | struct nfp_net *nn; |
145 | unsigned int type; |
146 | |
147 | if (!nfp_netdev_is_nfp_net(netdev)) |
148 | return; |
149 | nn = netdev_priv(dev: netdev); |
150 | if (nn->app != app) |
151 | return; |
152 | |
153 | for (type = 0; type < __NFP_REPR_TYPE_MAX; type++) { |
154 | struct nfp_reprs *reprs; |
155 | unsigned int i; |
156 | |
157 | reprs = rtnl_dereference(app->reprs[type]); |
158 | if (!reprs) |
159 | continue; |
160 | |
161 | for (i = 0; i < reprs->num_reprs; i++) { |
162 | struct net_device *repr; |
163 | |
164 | repr = rtnl_dereference(reprs->reprs[i]); |
165 | if (!repr) |
166 | continue; |
167 | |
168 | nfp_repr_transfer_features(netdev: repr, lower: netdev); |
169 | } |
170 | } |
171 | } |
172 | |
173 | static int |
174 | nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr) |
175 | { |
176 | struct net_device *netdev; |
177 | struct nfp_app *app; |
178 | |
179 | netdev = netdev_notifier_info_to_dev(info: ptr); |
180 | app = container_of(nb, struct nfp_app, netdev_nb); |
181 | |
182 | /* Handle events common code is interested in */ |
183 | switch (event) { |
184 | case NETDEV_FEAT_CHANGE: |
185 | nfp_app_netdev_feat_change(app, netdev); |
186 | break; |
187 | } |
188 | |
189 | /* Call offload specific handlers */ |
190 | if (app->type->netdev_event) |
191 | return app->type->netdev_event(app, netdev, event, ptr); |
192 | return NOTIFY_DONE; |
193 | } |
194 | |
195 | int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl) |
196 | { |
197 | int err; |
198 | |
199 | app->ctrl = ctrl; |
200 | |
201 | if (app->type->start) { |
202 | err = app->type->start(app); |
203 | if (err) |
204 | return err; |
205 | } |
206 | |
207 | app->netdev_nb.notifier_call = nfp_app_netdev_event; |
208 | err = register_netdevice_notifier(nb: &app->netdev_nb); |
209 | if (err) |
210 | goto err_app_stop; |
211 | |
212 | return 0; |
213 | |
214 | err_app_stop: |
215 | if (app->type->stop) |
216 | app->type->stop(app); |
217 | return err; |
218 | } |
219 | |
220 | void nfp_app_stop(struct nfp_app *app) |
221 | { |
222 | unregister_netdevice_notifier(nb: &app->netdev_nb); |
223 | |
224 | if (app->type->stop) |
225 | app->type->stop(app); |
226 | } |
227 | |
228 | struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id) |
229 | { |
230 | struct nfp_app *app; |
231 | |
232 | if (id >= ARRAY_SIZE(apps) || !apps[id]) { |
233 | nfp_err(pf->cpp, "unknown FW app ID 0x%02x, driver too old or support for FW not built in\n" , id); |
234 | return ERR_PTR(error: -EINVAL); |
235 | } |
236 | |
237 | if (WARN_ON(!apps[id]->name || !apps[id]->vnic_alloc)) |
238 | return ERR_PTR(error: -EINVAL); |
239 | if (WARN_ON(!apps[id]->ctrl_msg_rx && apps[id]->ctrl_msg_rx_raw)) |
240 | return ERR_PTR(error: -EINVAL); |
241 | |
242 | app = kzalloc(size: sizeof(*app), GFP_KERNEL); |
243 | if (!app) |
244 | return ERR_PTR(error: -ENOMEM); |
245 | |
246 | app->pf = pf; |
247 | app->cpp = pf->cpp; |
248 | app->pdev = pf->pdev; |
249 | app->type = apps[id]; |
250 | |
251 | return app; |
252 | } |
253 | |
254 | void nfp_app_free(struct nfp_app *app) |
255 | { |
256 | kfree(objp: app); |
257 | } |
258 | |