| /* |
| ************************************************************************** |
| * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all copies. |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| ************************************************************************** |
| */ |
| |
| #include "nss_data_plane.h" |
| #include "nss_core.h" |
| #include "nss_tx_rx_common.h" |
| #include "nss_data_plane_hal.h" |
| |
| /* |
| * nss_data_plane_param |
| */ |
| struct nss_data_plane_param nss_data_plane_params[NSS_DP_MAX_INTERFACES]; |
| |
| /* |
| * __nss_data_plane_init() |
| */ |
| static int __nss_data_plane_init(struct nss_dp_data_plane_ctx *dpc) |
| { |
| struct net_device *netdev = dpc->dev; |
| netdev->needed_headroom += 32; |
| return NSS_DP_SUCCESS; |
| } |
| |
| /* |
| * __nss_data_plane_open() |
| * Called by nss-dp to notify open to nss-fw |
| */ |
| static int __nss_data_plane_open(struct nss_dp_data_plane_ctx *dpc, uint32_t tx_desc_ring, uint32_t rx_desc_ring, uint32_t mode) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| |
| if (dp->notify_open) { |
| return NSS_DP_SUCCESS; |
| } |
| |
| if (nss_phys_if_open(dp->nss_ctx, tx_desc_ring, rx_desc_ring, mode, dp->if_num, dp->bypass_nw_process) == NSS_TX_SUCCESS) { |
| dp->notify_open = 1; |
| return NSS_DP_SUCCESS; |
| } |
| return NSS_DP_FAILURE; |
| } |
| |
| /* |
| * __nss_data_plane_close() |
| * Called by nss-dp to notify close to nss-fw |
| */ |
| static int __nss_data_plane_close(struct nss_dp_data_plane_ctx *dpc) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| |
| if (!dp->notify_open) { |
| return NSS_DP_SUCCESS; |
| } |
| |
| if (nss_phys_if_close(dp->nss_ctx, dp->if_num) == NSS_TX_SUCCESS) { |
| dp->notify_open = 0; |
| return NSS_DP_SUCCESS; |
| } |
| return NSS_DP_FAILURE; |
| } |
| |
| /* |
| * __nss_data_plane_link_state() |
| * Called by nss-dp to notify link state change to nss-fw |
| */ |
| static int __nss_data_plane_link_state(struct nss_dp_data_plane_ctx *dpc, uint32_t link_state) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| |
| return nss_phys_if_link_state(dp->nss_ctx, link_state, dp->if_num); |
| } |
| |
| /* |
| * __nss_data_plane_mac_addr() |
| * Called by nss-dp to set mac address |
| */ |
| static int __nss_data_plane_mac_addr(struct nss_dp_data_plane_ctx *dpc, uint8_t *addr) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| |
| return nss_phys_if_mac_addr(dp->nss_ctx, addr, dp->if_num); |
| } |
| |
| /* |
| * __nss_data_plane_change_mtu() |
| * Called by nss-dp to change mtu of a data plane |
| */ |
| static int __nss_data_plane_change_mtu(struct nss_dp_data_plane_ctx *dpc, uint32_t mtu) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| return nss_phys_if_change_mtu(dp->nss_ctx, mtu, dp->if_num); |
| } |
| |
| /* |
| * __nss_data_plane_pause_on_off() |
| * Called by nss-dp to enable/disable pause frames |
| */ |
| static int __nss_data_plane_pause_on_off(struct nss_dp_data_plane_ctx *dpc, uint32_t pause_on) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| |
| return nss_phys_if_pause_on_off(dp->nss_ctx, pause_on, dp->if_num); |
| } |
| |
| #ifdef NSS_DRV_QRFS_ENABLE |
| /* |
| * __nss_data_plane_rx_flow_steer() |
| * Called by nss-dp to set flow rule of a data plane |
| */ |
| static int __nss_data_plane_rx_flow_steer(struct nss_dp_data_plane_ctx *dpc, struct sk_buff *skb, |
| uint32_t cpu, bool is_add) |
| { |
| if (is_add) { |
| return nss_qrfs_set_flow_rule(skb, cpu, NSS_QRFS_MSG_FLOW_ADD); |
| } |
| |
| return nss_qrfs_set_flow_rule(skb, cpu, NSS_QRFS_MSG_FLOW_DELETE); |
| } |
| #endif |
| |
| /* |
| * __nss_data_plane_deinit() |
| * Place holder for nss-dp ops to free NSS data plane resources |
| */ |
| static int __nss_data_plane_deinit(struct nss_dp_data_plane_ctx *dpc) |
| { |
| /* |
| * TODO: Implement free up of NSS data plane resources |
| */ |
| return NSS_TX_SUCCESS; |
| } |
| |
| /* |
| * __nss_data_plane_buf() |
| * Called by nss-dp to pass a sk_buff for xmit |
| */ |
| static netdev_tx_t __nss_data_plane_buf(struct nss_dp_data_plane_ctx *dpc, struct sk_buff *skb) |
| { |
| struct nss_data_plane_param *dp = (struct nss_data_plane_param *)dpc; |
| int extra_head = dpc->dev->needed_headroom - skb_headroom(skb); |
| int extra_tail = 0; |
| nss_tx_status_t status; |
| struct net_device *dev = dpc->dev; |
| |
| if (skb->len < ETH_HLEN) { |
| nss_warning("skb->len ( %u ) < ETH_HLEN ( %u ) \n", skb->len, ETH_HLEN); |
| goto drop; |
| } |
| |
| if (skb->len > NSS_DATA_PLANE_MAX_PACKET_LEN) { |
| nss_warning("skb->len ( %u ) > Maximum packet length ( %u ) \n", |
| skb->len, NSS_DATA_PLANE_MAX_PACKET_LEN); |
| goto drop; |
| } |
| |
| if (skb_cloned(skb) || extra_head > 0) { |
| /* |
| * If it is a clone and headroom is already enough, |
| * We just make a copy and clear the clone flag. |
| */ |
| if (extra_head <= 0) |
| extra_head = extra_tail = 0; |
| /* |
| * If tailroom is enough to accommodate the added headroom, |
| * then allocate a buffer of same size and do relocations. |
| * It might help kmalloc_reserve() not double the size. |
| */ |
| if (skb->end - skb->tail >= extra_head) |
| extra_tail = -extra_head; |
| |
| if (pskb_expand_head(skb, extra_head, extra_tail, GFP_ATOMIC)) { |
| nss_warning("%px: Unable to expand skb for headroom\n", dp); |
| goto drop; |
| } |
| } |
| |
| status = nss_phys_if_buf(dp->nss_ctx, skb, dp->if_num); |
| if (likely(status == NSS_TX_SUCCESS)) { |
| return NETDEV_TX_OK; |
| } else if (status == NSS_TX_FAILURE_QUEUE) { |
| return NETDEV_TX_BUSY; |
| } |
| |
| drop: |
| dev_kfree_skb_any(skb); |
| dev->stats.tx_dropped++; |
| |
| return NETDEV_TX_OK; |
| } |
| |
| /* |
| * __nss_data_plane_set_features() |
| * Called by nss-dp to allow data plane to modify the set of features it supports |
| */ |
| static void __nss_data_plane_set_features(struct nss_dp_data_plane_ctx *dpc) |
| { |
| nss_data_plane_hal_set_features(dpc); |
| } |
| |
| /* |
| * nss offload data plane ops |
| */ |
| static struct nss_dp_data_plane_ops dp_ops = { |
| .init = __nss_data_plane_init, |
| .open = __nss_data_plane_open, |
| .close = __nss_data_plane_close, |
| .link_state = __nss_data_plane_link_state, |
| .mac_addr = __nss_data_plane_mac_addr, |
| .change_mtu = __nss_data_plane_change_mtu, |
| .xmit = __nss_data_plane_buf, |
| .set_features = __nss_data_plane_set_features, |
| .pause_on_off = __nss_data_plane_pause_on_off, |
| #ifdef NSS_DRV_QRFS_ENABLE |
| .rx_flow_steer = __nss_data_plane_rx_flow_steer, |
| #endif |
| .deinit = __nss_data_plane_deinit, |
| }; |
| |
| /* |
| * nss_data_plane_register_to_nss_dp() |
| */ |
| static bool nss_data_plane_register_to_nss_dp(struct nss_ctx_instance *nss_ctx, int if_num) |
| { |
| struct nss_data_plane_param *ndpp = &nss_data_plane_params[if_num]; |
| struct nss_top_instance *nss_top = nss_ctx->nss_top; |
| struct net_device *netdev; |
| bool is_open; |
| int core; |
| |
| netdev = nss_dp_get_netdev_by_nss_if_num(if_num); |
| if (!netdev) { |
| nss_info("%px: Platform don't have data plane%d enabled, \ |
| don't bring up nss_phys_if and don't register to nss-dp\n", |
| nss_ctx, if_num); |
| return false; |
| } |
| |
| is_open = nss_dp_is_in_open_state(netdev); |
| ndpp->dpc.dev = netdev; |
| ndpp->nss_ctx = nss_ctx; |
| ndpp->if_num = if_num; |
| ndpp->notify_open = 0; |
| ndpp->features = 0; |
| |
| /* |
| * Add data plane ops applicable to this SoC. |
| */ |
| nss_data_plane_hal_add_dp_ops(&dp_ops); |
| |
| /* |
| * Check if NSS NW processing to be bypassed for this data plane |
| */ |
| if (nss_skip_nw_process) { |
| ndpp->bypass_nw_process = 1; |
| } else { |
| ndpp->bypass_nw_process = 0; |
| } |
| |
| if (nss_dp_override_data_plane(netdev, &dp_ops, (struct nss_dp_data_plane_ctx *)ndpp) != NSS_DP_SUCCESS) { |
| nss_info("%px: Override nss-dp data plane for port %dfailed\n", nss_ctx, if_num); |
| return false; |
| } |
| |
| /* |
| * Setup the receive callback so that data pkts received form NSS-FW will |
| * be redirected to the nss-dp driver as we are overriding the data plane |
| */ |
| nss_top->phys_if_handler_id[if_num] = nss_ctx->id; |
| nss_phys_if_register_handler(nss_ctx, if_num); |
| |
| /* |
| * Packets recieved on physical interface can be exceptioned to HLOS |
| * from any NSS core so we need to register data plane for all |
| */ |
| for (core = 0; core < nss_top->num_nss; core++) { |
| nss_core_register_subsys_dp(&nss_top->nss[core], if_num, nss_dp_receive, NULL, NULL, netdev, ndpp->features); |
| } |
| |
| /* |
| * Now we are registered and our side is ready, if the data plane was opened, ask it to start again |
| */ |
| if (is_open) { |
| nss_dp_start_data_plane(netdev, (struct nss_dp_data_plane_ctx *)ndpp); |
| } |
| return true; |
| } |
| |
| /* |
| * nss_data_plane_unregister_from_nss_dp() |
| */ |
| static void nss_data_plane_unregister_from_nss_dp(int if_num) |
| { |
| /* |
| * Do any SoC specific un-registrations. |
| */ |
| nss_data_plane_hal_unregister(nss_data_plane_params[if_num].nss_ctx); |
| |
| nss_dp_restore_data_plane(nss_data_plane_params[if_num].dpc.dev); |
| nss_data_plane_params[if_num].dpc.dev = NULL; |
| nss_data_plane_params[if_num].nss_ctx = NULL; |
| nss_data_plane_params[if_num].if_num = 0; |
| nss_data_plane_params[if_num].notify_open = 0; |
| nss_data_plane_params[if_num].bypass_nw_process = 0; |
| } |
| |
| /* |
| * __nss_data_plane_register() |
| */ |
| static void __nss_data_plane_register(struct nss_ctx_instance *nss_ctx) |
| { |
| int i; |
| |
| for (i = NSS_DP_START_IFNUM; i < NSS_DP_MAX_INTERFACES; i++) { |
| if (!nss_data_plane_register_to_nss_dp(nss_ctx, i)) { |
| nss_warning("%px: Register data plane failed for data plane %d\n", nss_ctx, i); |
| } else { |
| nss_info("%px: Register data plan to data plane %d success\n", nss_ctx, i); |
| } |
| } |
| |
| /* |
| * Do any SoC specific registrations. |
| */ |
| nss_data_plane_hal_register(nss_ctx); |
| } |
| |
| /* |
| * __nss_data_plane_unregister() |
| */ |
| static void __nss_data_plane_unregister(void) |
| { |
| int i, core; |
| |
| for (core = 0; core < nss_top_main.num_nss; core++) { |
| for (i = NSS_DP_START_IFNUM; i < NSS_DP_MAX_INTERFACES; i++) { |
| if (nss_top_main.nss[core].subsys_dp_register[i].ndev) { |
| nss_data_plane_unregister_from_nss_dp(i); |
| nss_core_unregister_subsys_dp(&nss_top_main.nss[core], i); |
| } |
| } |
| } |
| } |
| |
| /* |
| * __nss_data_plane_stats_sync() |
| */ |
| static void __nss_data_plane_stats_sync(struct nss_phys_if_stats *stats, uint16_t interface) |
| { |
| nss_data_plane_hal_stats_sync(&nss_data_plane_params[interface], stats); |
| } |
| |
| /* |
| * __nss_data_plane_get_mtu_sz() |
| */ |
| static uint16_t __nss_data_plane_get_mtu_sz(uint16_t mtu) |
| { |
| return nss_data_plane_hal_get_mtu_sz(mtu); |
| } |
| |
| /* |
| * nss_data_plane_ops |
| */ |
| struct nss_data_plane_ops nss_data_plane_ops = { |
| .data_plane_register = &__nss_data_plane_register, |
| .data_plane_unregister = &__nss_data_plane_unregister, |
| .data_plane_stats_sync = &__nss_data_plane_stats_sync, |
| .data_plane_get_mtu_sz = &__nss_data_plane_get_mtu_sz, |
| }; |