196de2506SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
296de2506SJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
37ac9ebd5SJakub Kicinski
44c7787baSMark Brown #include <linux/bug.h>
5e1740fb6SJakub Kicinski #include <linux/lockdep.h>
6e1740fb6SJakub Kicinski #include <linux/rcupdate.h>
702082701SJakub Kicinski #include <linux/skbuff.h>
87ac9ebd5SJakub Kicinski #include <linux/slab.h>
97ac9ebd5SJakub Kicinski
108aa0cb00SJakub Kicinski #include "nfpcore/nfp_cpp.h"
1176abc0f6SJakub Kicinski #include "nfpcore/nfp_nffw.h"
127ac9ebd5SJakub Kicinski #include "nfp_app.h"
137ac9ebd5SJakub Kicinski #include "nfp_main.h"
149e4c2cfcSJakub Kicinski #include "nfp_net.h"
155de73ee4SSimon Horman #include "nfp_net_repr.h"
1621f31bc0SJakub Kicinski #include "nfp_port.h"
177ac9ebd5SJakub Kicinski
188aa0cb00SJakub Kicinski static const struct nfp_app_type *apps[] = {
192c4197a0SJakub Kicinski [NFP_APP_CORE_NIC] = &app_nic,
2043b45245SJakub Kicinski #ifdef CONFIG_BPF_SYSCALL
212c4197a0SJakub Kicinski [NFP_APP_BPF_NIC] = &app_bpf,
2243b45245SJakub Kicinski #else
2343b45245SJakub Kicinski [NFP_APP_BPF_NIC] = &app_nic,
2443b45245SJakub Kicinski #endif
2557ae676eSJakub Kicinski #ifdef CONFIG_NFP_APP_FLOWER
262c4197a0SJakub Kicinski [NFP_APP_FLOWER_NIC] = &app_flower,
2757ae676eSJakub Kicinski #endif
28c4c8f39aSJakub Kicinski #ifdef CONFIG_NFP_APP_ABM_NIC
29c4c8f39aSJakub Kicinski [NFP_APP_ACTIVE_BUFFER_MGMT_NIC] = &app_abm,
30c4c8f39aSJakub Kicinski #endif
318aa0cb00SJakub Kicinski };
328aa0cb00SJakub Kicinski
nfp_check_rhashtable_empty(void * ptr,void * arg)33c01d0efaSPieter Jansen van Vuuren void nfp_check_rhashtable_empty(void *ptr, void *arg)
34c01d0efaSPieter Jansen van Vuuren {
35c01d0efaSPieter Jansen van Vuuren WARN_ON_ONCE(1);
36c01d0efaSPieter Jansen van Vuuren }
37c01d0efaSPieter Jansen van Vuuren
nfp_app_from_netdev(struct net_device * netdev)389e4c2cfcSJakub Kicinski struct nfp_app *nfp_app_from_netdev(struct net_device *netdev)
399e4c2cfcSJakub Kicinski {
409e4c2cfcSJakub Kicinski if (nfp_netdev_is_nfp_net(netdev)) {
419e4c2cfcSJakub Kicinski struct nfp_net *nn = netdev_priv(netdev);
429e4c2cfcSJakub Kicinski
439e4c2cfcSJakub Kicinski return nn->app;
449e4c2cfcSJakub Kicinski }
459e4c2cfcSJakub Kicinski
469e4c2cfcSJakub Kicinski if (nfp_netdev_is_nfp_repr(netdev)) {
479e4c2cfcSJakub Kicinski struct nfp_repr *repr = netdev_priv(netdev);
489e4c2cfcSJakub Kicinski
499e4c2cfcSJakub Kicinski return repr->app;
509e4c2cfcSJakub Kicinski }
519e4c2cfcSJakub Kicinski
529e4c2cfcSJakub Kicinski WARN(1, "Unknown netdev type for nfp_app\n");
539e4c2cfcSJakub Kicinski
549e4c2cfcSJakub Kicinski return NULL;
559e4c2cfcSJakub Kicinski }
569e4c2cfcSJakub Kicinski
nfp_app_mip_name(struct nfp_app * app)5776abc0f6SJakub Kicinski const char *nfp_app_mip_name(struct nfp_app *app)
5876abc0f6SJakub Kicinski {
5976abc0f6SJakub Kicinski if (!app || !app->pf->mip)
6076abc0f6SJakub Kicinski return "";
6176abc0f6SJakub Kicinski return nfp_mip_name(app->pf->mip);
6276abc0f6SJakub Kicinski }
6376abc0f6SJakub Kicinski
nfp_app_ndo_init(struct net_device * netdev)644612bebfSJakub Kicinski int nfp_app_ndo_init(struct net_device *netdev)
654612bebfSJakub Kicinski {
664612bebfSJakub Kicinski struct nfp_app *app = nfp_app_from_netdev(netdev);
674612bebfSJakub Kicinski
684612bebfSJakub Kicinski if (!app || !app->type->ndo_init)
694612bebfSJakub Kicinski return 0;
704612bebfSJakub Kicinski return app->type->ndo_init(app, netdev);
714612bebfSJakub Kicinski }
724612bebfSJakub Kicinski
nfp_app_ndo_uninit(struct net_device * netdev)734612bebfSJakub Kicinski void nfp_app_ndo_uninit(struct net_device *netdev)
744612bebfSJakub Kicinski {
754612bebfSJakub Kicinski struct nfp_app *app = nfp_app_from_netdev(netdev);
764612bebfSJakub Kicinski
774612bebfSJakub Kicinski if (app && app->type->ndo_uninit)
784612bebfSJakub Kicinski app->type->ndo_uninit(app, netdev);
794612bebfSJakub Kicinski }
804612bebfSJakub Kicinski
nfp_app_port_get_stats(struct nfp_port * port,u64 * data)8121f31bc0SJakub Kicinski u64 *nfp_app_port_get_stats(struct nfp_port *port, u64 *data)
8221f31bc0SJakub Kicinski {
8321f31bc0SJakub Kicinski if (!port || !port->app || !port->app->type->port_get_stats)
8421f31bc0SJakub Kicinski return data;
8521f31bc0SJakub Kicinski return port->app->type->port_get_stats(port->app, port, data);
8621f31bc0SJakub Kicinski }
8721f31bc0SJakub Kicinski
nfp_app_port_get_stats_count(struct nfp_port * port)8821f31bc0SJakub Kicinski int nfp_app_port_get_stats_count(struct nfp_port *port)
8921f31bc0SJakub Kicinski {
9021f31bc0SJakub Kicinski if (!port || !port->app || !port->app->type->port_get_stats_count)
9121f31bc0SJakub Kicinski return 0;
9221f31bc0SJakub Kicinski return port->app->type->port_get_stats_count(port->app, port);
9321f31bc0SJakub Kicinski }
9421f31bc0SJakub Kicinski
nfp_app_port_get_stats_strings(struct nfp_port * port,u8 * data)9521f31bc0SJakub Kicinski u8 *nfp_app_port_get_stats_strings(struct nfp_port *port, u8 *data)
9621f31bc0SJakub Kicinski {
9721f31bc0SJakub Kicinski if (!port || !port->app || !port->app->type->port_get_stats_strings)
9821f31bc0SJakub Kicinski return data;
9921f31bc0SJakub Kicinski return port->app->type->port_get_stats_strings(port->app, port, data);
10021f31bc0SJakub Kicinski }
10121f31bc0SJakub Kicinski
102948faa46SSimon Horman struct sk_buff *
nfp_app_ctrl_msg_alloc(struct nfp_app * app,unsigned int size,gfp_t priority)103948faa46SSimon Horman nfp_app_ctrl_msg_alloc(struct nfp_app *app, unsigned int size, gfp_t priority)
10402082701SJakub Kicinski {
10502082701SJakub Kicinski struct sk_buff *skb;
10602082701SJakub Kicinski
10702082701SJakub Kicinski if (nfp_app_ctrl_has_meta(app))
10802082701SJakub Kicinski size += 8;
10902082701SJakub Kicinski
110948faa46SSimon Horman skb = alloc_skb(size, priority);
11102082701SJakub Kicinski if (!skb)
11202082701SJakub Kicinski return NULL;
11302082701SJakub Kicinski
11402082701SJakub Kicinski if (nfp_app_ctrl_has_meta(app))
11502082701SJakub Kicinski skb_reserve(skb, 8);
11602082701SJakub Kicinski
11702082701SJakub Kicinski return skb;
11802082701SJakub Kicinski }
11902082701SJakub Kicinski
1205de73ee4SSimon Horman struct nfp_reprs *
nfp_reprs_get_locked(struct nfp_app * app,enum nfp_repr_type type)121e1740fb6SJakub Kicinski nfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type)
122e1740fb6SJakub Kicinski {
123e1740fb6SJakub Kicinski return rcu_dereference_protected(app->reprs[type],
1248a38f2ccSJakub Kicinski nfp_app_is_locked(app));
125e1740fb6SJakub Kicinski }
126e1740fb6SJakub Kicinski
127e1740fb6SJakub Kicinski struct nfp_reprs *
nfp_app_reprs_set(struct nfp_app * app,enum nfp_repr_type type,struct nfp_reprs * reprs)1285de73ee4SSimon Horman nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type,
1295de73ee4SSimon Horman struct nfp_reprs *reprs)
1305de73ee4SSimon Horman {
1315de73ee4SSimon Horman struct nfp_reprs *old;
1325de73ee4SSimon Horman
133e1740fb6SJakub Kicinski old = nfp_reprs_get_locked(app, type);
13471844facSJakub Kicinski rtnl_lock();
1355de73ee4SSimon Horman rcu_assign_pointer(app->reprs[type], reprs);
13671844facSJakub Kicinski rtnl_unlock();
1375de73ee4SSimon Horman
1385de73ee4SSimon Horman return old;
1395de73ee4SSimon Horman }
1405de73ee4SSimon Horman
14151a6588eSJakub Kicinski static void
nfp_app_netdev_feat_change(struct nfp_app * app,struct net_device * netdev)14251a6588eSJakub Kicinski nfp_app_netdev_feat_change(struct nfp_app *app, struct net_device *netdev)
14351a6588eSJakub Kicinski {
14451a6588eSJakub Kicinski struct nfp_net *nn;
14551a6588eSJakub Kicinski unsigned int type;
14651a6588eSJakub Kicinski
14751a6588eSJakub Kicinski if (!nfp_netdev_is_nfp_net(netdev))
14851a6588eSJakub Kicinski return;
14951a6588eSJakub Kicinski nn = netdev_priv(netdev);
15051a6588eSJakub Kicinski if (nn->app != app)
15151a6588eSJakub Kicinski return;
15251a6588eSJakub Kicinski
15351a6588eSJakub Kicinski for (type = 0; type < __NFP_REPR_TYPE_MAX; type++) {
15451a6588eSJakub Kicinski struct nfp_reprs *reprs;
15551a6588eSJakub Kicinski unsigned int i;
15651a6588eSJakub Kicinski
15751a6588eSJakub Kicinski reprs = rtnl_dereference(app->reprs[type]);
15851a6588eSJakub Kicinski if (!reprs)
15951a6588eSJakub Kicinski continue;
16051a6588eSJakub Kicinski
16151a6588eSJakub Kicinski for (i = 0; i < reprs->num_reprs; i++) {
16251a6588eSJakub Kicinski struct net_device *repr;
16351a6588eSJakub Kicinski
16451a6588eSJakub Kicinski repr = rtnl_dereference(reprs->reprs[i]);
16551a6588eSJakub Kicinski if (!repr)
16651a6588eSJakub Kicinski continue;
16751a6588eSJakub Kicinski
16851a6588eSJakub Kicinski nfp_repr_transfer_features(repr, netdev);
16951a6588eSJakub Kicinski }
17051a6588eSJakub Kicinski }
17151a6588eSJakub Kicinski }
17251a6588eSJakub Kicinski
1733e333590SJakub Kicinski static int
nfp_app_netdev_event(struct notifier_block * nb,unsigned long event,void * ptr)1743e333590SJakub Kicinski nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr)
1753e333590SJakub Kicinski {
1763e333590SJakub Kicinski struct net_device *netdev;
1773e333590SJakub Kicinski struct nfp_app *app;
1783e333590SJakub Kicinski
1793e333590SJakub Kicinski netdev = netdev_notifier_info_to_dev(ptr);
1803e333590SJakub Kicinski app = container_of(nb, struct nfp_app, netdev_nb);
1813e333590SJakub Kicinski
18251a6588eSJakub Kicinski /* Handle events common code is interested in */
18351a6588eSJakub Kicinski switch (event) {
18451a6588eSJakub Kicinski case NETDEV_FEAT_CHANGE:
18551a6588eSJakub Kicinski nfp_app_netdev_feat_change(app, netdev);
18651a6588eSJakub Kicinski break;
18751a6588eSJakub Kicinski }
18851a6588eSJakub Kicinski
18951a6588eSJakub Kicinski /* Call offload specific handlers */
1903e333590SJakub Kicinski if (app->type->netdev_event)
1913e333590SJakub Kicinski return app->type->netdev_event(app, netdev, event, ptr);
1923e333590SJakub Kicinski return NOTIFY_DONE;
1933e333590SJakub Kicinski }
1943e333590SJakub Kicinski
nfp_app_start(struct nfp_app * app,struct nfp_net * ctrl)1953e333590SJakub Kicinski int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl)
1963e333590SJakub Kicinski {
1973e333590SJakub Kicinski int err;
1983e333590SJakub Kicinski
1993e333590SJakub Kicinski app->ctrl = ctrl;
2003e333590SJakub Kicinski
2013e333590SJakub Kicinski if (app->type->start) {
2023e333590SJakub Kicinski err = app->type->start(app);
2033e333590SJakub Kicinski if (err)
2043e333590SJakub Kicinski return err;
2053e333590SJakub Kicinski }
2063e333590SJakub Kicinski
2073e333590SJakub Kicinski app->netdev_nb.notifier_call = nfp_app_netdev_event;
2083e333590SJakub Kicinski err = register_netdevice_notifier(&app->netdev_nb);
2093e333590SJakub Kicinski if (err)
2103e333590SJakub Kicinski goto err_app_stop;
2113e333590SJakub Kicinski
2123e333590SJakub Kicinski return 0;
2133e333590SJakub Kicinski
2143e333590SJakub Kicinski err_app_stop:
2153e333590SJakub Kicinski if (app->type->stop)
2163e333590SJakub Kicinski app->type->stop(app);
2173e333590SJakub Kicinski return err;
2183e333590SJakub Kicinski }
2193e333590SJakub Kicinski
nfp_app_stop(struct nfp_app * app)2203e333590SJakub Kicinski void nfp_app_stop(struct nfp_app *app)
2213e333590SJakub Kicinski {
2223e333590SJakub Kicinski unregister_netdevice_notifier(&app->netdev_nb);
2233e333590SJakub Kicinski
2243e333590SJakub Kicinski if (app->type->stop)
2253e333590SJakub Kicinski app->type->stop(app);
2263e333590SJakub Kicinski }
2273e333590SJakub Kicinski
nfp_app_alloc(struct nfp_pf * pf,enum nfp_app_id id)2288aa0cb00SJakub Kicinski struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id)
2297ac9ebd5SJakub Kicinski {
2307ac9ebd5SJakub Kicinski struct nfp_app *app;
2318aa0cb00SJakub Kicinski
2322c4197a0SJakub Kicinski if (id >= ARRAY_SIZE(apps) || !apps[id]) {
233*ef2a95dbSJustin Stitt nfp_err(pf->cpp, "unknown FW app ID 0x%02x, driver too old or support for FW not built in\n", id);
2348aa0cb00SJakub Kicinski return ERR_PTR(-EINVAL);
2358aa0cb00SJakub Kicinski }
2368aa0cb00SJakub Kicinski
2372c4197a0SJakub Kicinski if (WARN_ON(!apps[id]->name || !apps[id]->vnic_alloc))
2388aa0cb00SJakub Kicinski return ERR_PTR(-EINVAL);
23979ca38e8SJakub Kicinski if (WARN_ON(!apps[id]->ctrl_msg_rx && apps[id]->ctrl_msg_rx_raw))
24079ca38e8SJakub Kicinski return ERR_PTR(-EINVAL);
2417ac9ebd5SJakub Kicinski
2427ac9ebd5SJakub Kicinski app = kzalloc(sizeof(*app), GFP_KERNEL);
2437ac9ebd5SJakub Kicinski if (!app)
2447ac9ebd5SJakub Kicinski return ERR_PTR(-ENOMEM);
2457ac9ebd5SJakub Kicinski
2467ac9ebd5SJakub Kicinski app->pf = pf;
2477ac9ebd5SJakub Kicinski app->cpp = pf->cpp;
2487ac9ebd5SJakub Kicinski app->pdev = pf->pdev;
2492c4197a0SJakub Kicinski app->type = apps[id];
2507ac9ebd5SJakub Kicinski
2517ac9ebd5SJakub Kicinski return app;
2527ac9ebd5SJakub Kicinski }
2537ac9ebd5SJakub Kicinski
nfp_app_free(struct nfp_app * app)2547ac9ebd5SJakub Kicinski void nfp_app_free(struct nfp_app *app)
2557ac9ebd5SJakub Kicinski {
2567ac9ebd5SJakub Kicinski kfree(app);
2577ac9ebd5SJakub Kicinski }
258