162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bug.h> 562306a36Sopenharmony_ci#include <linux/lockdep.h> 662306a36Sopenharmony_ci#include <linux/rcupdate.h> 762306a36Sopenharmony_ci#include <linux/skbuff.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "nfpcore/nfp_cpp.h" 1162306a36Sopenharmony_ci#include "nfpcore/nfp_nffw.h" 1262306a36Sopenharmony_ci#include "nfp_app.h" 1362306a36Sopenharmony_ci#include "nfp_main.h" 1462306a36Sopenharmony_ci#include "nfp_net.h" 1562306a36Sopenharmony_ci#include "nfp_net_repr.h" 1662306a36Sopenharmony_ci#include "nfp_port.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const struct nfp_app_type *apps[] = { 1962306a36Sopenharmony_ci [NFP_APP_CORE_NIC] = &app_nic, 2062306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 2162306a36Sopenharmony_ci [NFP_APP_BPF_NIC] = &app_bpf, 2262306a36Sopenharmony_ci#else 2362306a36Sopenharmony_ci [NFP_APP_BPF_NIC] = &app_nic, 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci#ifdef CONFIG_NFP_APP_FLOWER 2662306a36Sopenharmony_ci [NFP_APP_FLOWER_NIC] = &app_flower, 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci#ifdef CONFIG_NFP_APP_ABM_NIC 2962306a36Sopenharmony_ci [NFP_APP_ACTIVE_BUFFER_MGMT_NIC] = &app_abm, 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid nfp_check_rhashtable_empty(void *ptr, void *arg) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci WARN_ON_ONCE(1); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct nfp_app *nfp_app_from_netdev(struct net_device *netdev) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci if (nfp_netdev_is_nfp_net(netdev)) { 4162306a36Sopenharmony_ci struct nfp_net *nn = netdev_priv(netdev); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return nn->app; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (nfp_netdev_is_nfp_repr(netdev)) { 4762306a36Sopenharmony_ci struct nfp_repr *repr = netdev_priv(netdev); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return repr->app; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci WARN(1, "Unknown netdev type for nfp_app\n"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return NULL; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciconst char *nfp_app_mip_name(struct nfp_app *app) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (!app || !app->pf->mip) 6062306a36Sopenharmony_ci return ""; 6162306a36Sopenharmony_ci return nfp_mip_name(app->pf->mip); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciint nfp_app_ndo_init(struct net_device *netdev) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct nfp_app *app = nfp_app_from_netdev(netdev); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!app || !app->type->ndo_init) 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci return app->type->ndo_init(app, netdev); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_civoid nfp_app_ndo_uninit(struct net_device *netdev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct nfp_app *app = nfp_app_from_netdev(netdev); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (app && app->type->ndo_uninit) 7862306a36Sopenharmony_ci app->type->ndo_uninit(app, netdev); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciu64 *nfp_app_port_get_stats(struct nfp_port *port, u64 *data) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci if (!port || !port->app || !port->app->type->port_get_stats) 8462306a36Sopenharmony_ci return data; 8562306a36Sopenharmony_ci return port->app->type->port_get_stats(port->app, port, data); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciint nfp_app_port_get_stats_count(struct nfp_port *port) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci if (!port || !port->app || !port->app->type->port_get_stats_count) 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci return port->app->type->port_get_stats_count(port->app, port); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciu8 *nfp_app_port_get_stats_strings(struct nfp_port *port, u8 *data) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci if (!port || !port->app || !port->app->type->port_get_stats_strings) 9862306a36Sopenharmony_ci return data; 9962306a36Sopenharmony_ci return port->app->type->port_get_stats_strings(port->app, port, data); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct sk_buff * 10362306a36Sopenharmony_cinfp_app_ctrl_msg_alloc(struct nfp_app *app, unsigned int size, gfp_t priority) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct sk_buff *skb; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (nfp_app_ctrl_has_meta(app)) 10862306a36Sopenharmony_ci size += 8; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci skb = alloc_skb(size, priority); 11162306a36Sopenharmony_ci if (!skb) 11262306a36Sopenharmony_ci return NULL; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (nfp_app_ctrl_has_meta(app)) 11562306a36Sopenharmony_ci skb_reserve(skb, 8); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return skb; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct nfp_reprs * 12162306a36Sopenharmony_cinfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return rcu_dereference_protected(app->reprs[type], 12462306a36Sopenharmony_ci nfp_app_is_locked(app)); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistruct nfp_reprs * 12862306a36Sopenharmony_cinfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type, 12962306a36Sopenharmony_ci struct nfp_reprs *reprs) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct nfp_reprs *old; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci old = nfp_reprs_get_locked(app, type); 13462306a36Sopenharmony_ci rtnl_lock(); 13562306a36Sopenharmony_ci rcu_assign_pointer(app->reprs[type], reprs); 13662306a36Sopenharmony_ci rtnl_unlock(); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return old; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void 14262306a36Sopenharmony_cinfp_app_netdev_feat_change(struct nfp_app *app, struct net_device *netdev) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct nfp_net *nn; 14562306a36Sopenharmony_ci unsigned int type; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!nfp_netdev_is_nfp_net(netdev)) 14862306a36Sopenharmony_ci return; 14962306a36Sopenharmony_ci nn = netdev_priv(netdev); 15062306a36Sopenharmony_ci if (nn->app != app) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci for (type = 0; type < __NFP_REPR_TYPE_MAX; type++) { 15462306a36Sopenharmony_ci struct nfp_reprs *reprs; 15562306a36Sopenharmony_ci unsigned int i; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci reprs = rtnl_dereference(app->reprs[type]); 15862306a36Sopenharmony_ci if (!reprs) 15962306a36Sopenharmony_ci continue; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < reprs->num_reprs; i++) { 16262306a36Sopenharmony_ci struct net_device *repr; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci repr = rtnl_dereference(reprs->reprs[i]); 16562306a36Sopenharmony_ci if (!repr) 16662306a36Sopenharmony_ci continue; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci nfp_repr_transfer_features(repr, netdev); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int 17462306a36Sopenharmony_cinfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct net_device *netdev; 17762306a36Sopenharmony_ci struct nfp_app *app; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci netdev = netdev_notifier_info_to_dev(ptr); 18062306a36Sopenharmony_ci app = container_of(nb, struct nfp_app, netdev_nb); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Handle events common code is interested in */ 18362306a36Sopenharmony_ci switch (event) { 18462306a36Sopenharmony_ci case NETDEV_FEAT_CHANGE: 18562306a36Sopenharmony_ci nfp_app_netdev_feat_change(app, netdev); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Call offload specific handlers */ 19062306a36Sopenharmony_ci if (app->type->netdev_event) 19162306a36Sopenharmony_ci return app->type->netdev_event(app, netdev, event, ptr); 19262306a36Sopenharmony_ci return NOTIFY_DONE; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci int err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci app->ctrl = ctrl; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (app->type->start) { 20262306a36Sopenharmony_ci err = app->type->start(app); 20362306a36Sopenharmony_ci if (err) 20462306a36Sopenharmony_ci return err; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci app->netdev_nb.notifier_call = nfp_app_netdev_event; 20862306a36Sopenharmony_ci err = register_netdevice_notifier(&app->netdev_nb); 20962306a36Sopenharmony_ci if (err) 21062306a36Sopenharmony_ci goto err_app_stop; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cierr_app_stop: 21562306a36Sopenharmony_ci if (app->type->stop) 21662306a36Sopenharmony_ci app->type->stop(app); 21762306a36Sopenharmony_ci return err; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_civoid nfp_app_stop(struct nfp_app *app) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci unregister_netdevice_notifier(&app->netdev_nb); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (app->type->stop) 22562306a36Sopenharmony_ci app->type->stop(app); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct nfp_app *app; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (id >= ARRAY_SIZE(apps) || !apps[id]) { 23362306a36Sopenharmony_ci nfp_err(pf->cpp, "unknown FW app ID 0x%02x, driver too old or support for FW not built in\n", id); 23462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (WARN_ON(!apps[id]->name || !apps[id]->vnic_alloc)) 23862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 23962306a36Sopenharmony_ci if (WARN_ON(!apps[id]->ctrl_msg_rx && apps[id]->ctrl_msg_rx_raw)) 24062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci app = kzalloc(sizeof(*app), GFP_KERNEL); 24362306a36Sopenharmony_ci if (!app) 24462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci app->pf = pf; 24762306a36Sopenharmony_ci app->cpp = pf->cpp; 24862306a36Sopenharmony_ci app->pdev = pf->pdev; 24962306a36Sopenharmony_ci app->type = apps[id]; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return app; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_civoid nfp_app_free(struct nfp_app *app) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci kfree(app); 25762306a36Sopenharmony_ci} 258