/** * nfp_flower_get_stats() - Populates flow stats obtained from hardware. * @app: Pointer to the APP handle * @netdev: Netdev structure. * @flow: TC flower classifier offload structure * @egress: Netdev is the egress dev. * * Populates a flow statistics structure which which corresponds to a * specific flow. * * Return: negative value on error, 0 if stats populated successfully. */ static int nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev, struct tc_cls_flower_offload *flow, bool egress) { struct nfp_fl_payload *nfp_flow; struct net_device *ingr_dev; ingr_dev = egress ? NULL : netdev; nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev, NFP_FL_STATS_CTX_DONT_CARE); if (!nfp_flow) return -EINVAL; if (nfp_flow->ingress_offload && egress) return 0; spin_lock_bh(&nfp_flow->lock); tcf_exts_stats_update(flow->exts, nfp_flow->stats.bytes, nfp_flow->stats.pkts, nfp_flow->stats.used); nfp_flow->stats.pkts = 0; nfp_flow->stats.bytes = 0; spin_unlock_bh(&nfp_flow->lock); return 0; }
int nfp_compile_flow_metadata(struct nfp_app *app, struct tc_cls_flower_offload *flow, struct nfp_fl_payload *nfp_flow, struct net_device *netdev) { struct nfp_flower_priv *priv = app->priv; struct nfp_fl_payload *check_entry; u8 new_mask_id; u32 stats_cxt; if (nfp_get_stats_entry(app, &stats_cxt)) return -ENOENT; nfp_flow->meta.host_ctx_id = cpu_to_be32(stats_cxt); nfp_flow->meta.host_cookie = cpu_to_be64(flow->cookie); nfp_flow->ingress_dev = netdev; new_mask_id = 0; if (!nfp_check_mask_add(app, nfp_flow->mask_data, nfp_flow->meta.mask_len, &nfp_flow->meta.flags, &new_mask_id)) { if (nfp_release_stats_entry(app, stats_cxt)) return -EINVAL; return -ENOENT; } nfp_flow->meta.flow_version = cpu_to_be64(priv->flower_version); priv->flower_version++; /* Update flow payload with mask ids. */ nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id; priv->stats[stats_cxt].pkts = 0; priv->stats[stats_cxt].bytes = 0; priv->stats[stats_cxt].used = jiffies; check_entry = nfp_flower_search_fl_table(app, flow->cookie, netdev); if (check_entry) { if (nfp_release_stats_entry(app, stats_cxt)) return -EINVAL; if (!nfp_check_mask_remove(app, nfp_flow->mask_data, nfp_flow->meta.mask_len, NULL, &new_mask_id)) return -EINVAL; return -EEXIST; } return 0; }
/** * nfp_flower_del_offload() - Removes a flow from hardware. * @app: Pointer to the APP handle * @netdev: netdev structure. * @flow: TC flower classifier offload structure * @egress: Netdev is the egress dev. * * Removes a flow from the repeated hash structure and clears the * action payload. * * Return: negative value on error, 0 if removed successfully. */ static int nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev, struct tc_cls_flower_offload *flow, bool egress) { struct nfp_port *port = nfp_port_from_netdev(netdev); struct nfp_fl_payload *nfp_flow; struct net_device *ingr_dev; int err; ingr_dev = egress ? NULL : netdev; nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev, NFP_FL_STATS_CTX_DONT_CARE); if (!nfp_flow) return egress ? 0 : -ENOENT; err = nfp_modify_flow_metadata(app, nfp_flow); if (err) goto err_free_flow; if (nfp_flow->nfp_tun_ipv4_addr) nfp_tunnel_del_ipv4_off(app, nfp_flow->nfp_tun_ipv4_addr); err = nfp_flower_xmit_flow(netdev, nfp_flow, NFP_FLOWER_CMSG_TYPE_FLOW_DEL); if (err) goto err_free_flow; err_free_flow: hash_del_rcu(&nfp_flow->link); port->tc_offload_cnt--; kfree(nfp_flow->action_data); kfree(nfp_flow->mask_data); kfree(nfp_flow->unmasked_data); kfree_rcu(nfp_flow, rcu); return err; }
/** * nfp_flower_add_offload() - Adds a new flow to hardware. * @app: Pointer to the APP handle * @netdev: netdev structure. * @flow: TC flower classifier offload structure. * @egress: NFP netdev is the egress. * * Adds a new flow to the repeated hash structure and action payload. * * Return: negative value on error, 0 if configured successfully. */ static int nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev, struct tc_cls_flower_offload *flow, bool egress) { enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE; struct nfp_port *port = nfp_port_from_netdev(netdev); struct nfp_flower_priv *priv = app->priv; struct nfp_fl_payload *flow_pay; struct nfp_fl_key_ls *key_layer; struct net_device *ingr_dev; int err; ingr_dev = egress ? NULL : netdev; flow_pay = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev, NFP_FL_STATS_CTX_DONT_CARE); if (flow_pay) { /* Ignore as duplicate if it has been added by different cb. */ if (flow_pay->ingress_offload && egress) return 0; else return -EOPNOTSUPP; } key_layer = kmalloc(sizeof(*key_layer), GFP_KERNEL); if (!key_layer) return -ENOMEM; err = nfp_flower_calculate_key_layers(app, key_layer, flow, egress, &tun_type); if (err) goto err_free_key_ls; flow_pay = nfp_flower_allocate_new(key_layer, egress); if (!flow_pay) { err = -ENOMEM; goto err_free_key_ls; } flow_pay->ingress_dev = egress ? NULL : netdev; err = nfp_flower_compile_flow_match(flow, key_layer, netdev, flow_pay, tun_type); if (err) goto err_destroy_flow; err = nfp_flower_compile_action(app, flow, netdev, flow_pay); if (err) goto err_destroy_flow; err = nfp_compile_flow_metadata(app, flow, flow_pay, flow_pay->ingress_dev); if (err) goto err_destroy_flow; err = nfp_flower_xmit_flow(netdev, flow_pay, NFP_FLOWER_CMSG_TYPE_FLOW_ADD); if (err) goto err_destroy_flow; INIT_HLIST_NODE(&flow_pay->link); flow_pay->tc_flower_cookie = flow->cookie; hash_add_rcu(priv->flow_table, &flow_pay->link, flow->cookie); port->tc_offload_cnt++; /* Deallocate flow payload when flower rule has been destroyed. */ kfree(key_layer); return 0; err_destroy_flow: kfree(flow_pay->action_data); kfree(flow_pay->mask_data); kfree(flow_pay->unmasked_data); kfree(flow_pay); err_free_key_ls: kfree(key_layer); return err; }