From f6570e40105cdd0f3d6126cc8bba38099edac4c2 Mon Sep 17 00:00:00 2001 From: rakesh goyal Date: Tue, 30 Jun 2020 19:46:34 +0530 Subject: [PATCH] nvethernet: Add support for 2 step timestamp read - On transmit complete callback -- If OSI_TXDONE_CX_TS_DELAY flag is set in Tx done flag, store buffer pointer along with pkt Id in its internal array/list - On Common interrupt: -- Call osi to handle common interrupt. - On trasmit complete interrupt: -- Parse through internal array/list for pending TS to read, by issuing OSI_CMD_GET_TS ioctl for pkt_id. Loop until list is cleared or error is received. -- On interface up Remove old timestamp of channels initialized. Signed-off-by: Rakesh Goyal Change-Id: I5104277de66dd240023f921fa5f7b4fcc035f74d Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2535962 Reviewed-by: svc_kernel_abi Reviewed-by: Nagarjuna Kristam Reviewed-by: Bhadram Varka Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- .../ethernet/nvidia/nvethernet/ether_linux.c | 134 +++++++++++++++++- .../ethernet/nvidia/nvethernet/ether_linux.h | 31 ++++ drivers/net/ethernet/nvidia/nvethernet/osd.c | 50 ++++++- 3 files changed, 210 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 34457aea..1070fca9 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -18,6 +18,78 @@ #include #include "ether_linux.h" +/** + * @brief Gets timestamp and update skb + * + * Algorithm: + * - Parse through tx_ts_skb_head. + * - Issue osi_handle_ioctl(OSI_CMD_GET_TX_TS) to read timestamp. + * - Update skb with timestamp and give to network stack + * - Free skb and node. + * + * @param[in] work: Work to handle SKB list update + */ +static void ether_get_tx_ts(struct work_struct *work) +{ + struct ether_priv_data *pdata = container_of(work, + struct ether_priv_data, tx_ts_work); + struct list_head *head_node, *temp_head_node; + struct skb_shared_hwtstamps shhwtstamp; + struct osi_ioctl ioctl_data = {}; + unsigned long long nsec = 0x0; + struct ether_tx_ts_skb_list *pnode; + int ret = -1; + + if (list_empty(&pdata->tx_ts_skb_head)) { + return; + } + + list_for_each_safe(head_node, temp_head_node, + &pdata->tx_ts_skb_head) { + pnode = list_entry(head_node, + struct ether_tx_ts_skb_list, + list_head); + memset(&shhwtstamp, 0, + sizeof(struct skb_shared_hwtstamps)); + + ioctl_data.cmd = OSI_CMD_GET_TX_TS; + ioctl_data.tx_ts.pkt_id = pnode->pktid; + ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data); + if (ret == 0) { + /* get time stamp form ethernet server */ + dev_dbg(pdata->dev, "%s() pktid = %x, skb = %p\n", + __func__, pnode->pktid, pnode->skb); + + if ((ioctl_data.tx_ts.nsec & OSI_MAC_TCR_TXTSSMIS) == + OSI_MAC_TCR_TXTSSMIS) { + dev_warn(pdata->dev, + "No valid time for skb, removed\n"); + goto update_skb; + } + + nsec = ioctl_data.tx_ts.sec * ETHER_ONESEC_NENOSEC + + ioctl_data.tx_ts.nsec; + + /* pass tstamp to stack */ + shhwtstamp.hwtstamp = ns_to_ktime(nsec); + if (pnode->skb != NULL) { + skb_tstamp_tx(pnode->skb, &shhwtstamp); + } + +update_skb: + if (pnode->skb != NULL) { + dev_consume_skb_any(pnode->skb); + } + + list_del(head_node); + pnode->in_use = OSI_DISABLE; + + } else { + dev_dbg(pdata->dev, "Unable to retrieve TS from OSI\n"); + } + } +} + /** @brief invalidate local l2 address list * * Algorithm: Invalidate all nodes in local address link list @@ -2261,7 +2333,8 @@ static int ether_open(struct net_device *dev) struct ether_priv_data *pdata = netdev_priv(dev); struct osi_core_priv_data *osi_core = pdata->osi_core; struct osi_ioctl ioctl_data = {}; - int ret = 0; + unsigned int chan = 0x0; + int ret = 0, i; /* Reset the PHY */ if (gpio_is_valid(pdata->phy_reset)) { @@ -2369,12 +2442,29 @@ static int ether_open(struct net_device *dev) goto err_hw_init; } + for (i = 0; i < pdata->osi_dma->num_dma_chans; i++) { + chan = pdata->osi_dma->dma_chans[i]; + ioctl_data.cmd = OSI_CMD_FREE_TS; + if ((pdata->osi_dma->ptp_flag & OSI_PTP_SYNC_ONESTEP) == + OSI_PTP_SYNC_ONESTEP) { + ioctl_data.arg1_u32 = OSI_NONE; + } else { + ioctl_data.arg1_u32 = chan; + } + + ret = osi_handle_ioctl(osi_core, &ioctl_data); + if (ret < 0) { + dev_err(&dev->dev, + "%s: failed to free TX TS for channel %d\n", + __func__, chan); + goto err_hw_init; + } + } + /* As all registers reset as part of ether_close(), reset private * structure variable as well */ pdata->vlan_hash_filtering = OSI_PERFECT_FILTER_MODE; pdata->l2_filtering_mode = OSI_PERFECT_FILTER_MODE; - /* Set default PTP mode as Two step */ - pdata->osi_dma->ptp_flag = OSI_PTP_SYNC_TWOSTEP; /* Initialize PTP */ ret = ether_ptp_init(pdata); @@ -2567,6 +2657,37 @@ static inline void ether_delete_l2_filter(struct ether_priv_data *pdata) } } +/** + * @brief Call to delete nodes form tx timestamp skb list + * + * Algorithm: + * - Stop work queue + * - Delete nodes from link list + * + * @param[in] pdata: Pointer to private data structure. + */ +static inline void ether_flush_tx_ts_skb_list(struct ether_priv_data *pdata) +{ + struct ether_tx_ts_skb_list *pnode; + struct list_head *head_node, *temp_head_node; + + /* stop workqueue */ + cancel_work_sync(&pdata->tx_ts_work); + + /* Delete nodes from list and rest static memory for reuse */ + if (!list_empty(&pdata->tx_ts_skb_head)) { + list_for_each_safe(head_node, temp_head_node, + &pdata->tx_ts_skb_head) { + pnode = list_entry(head_node, + struct ether_tx_ts_skb_list, + list_head); + dev_kfree_skb(pnode->skb); + list_del(head_node); + pnode->in_use = OSI_DISABLE; + } + } +} + /** * @brief Call back to handle bring down of Ethernet interface * @@ -2649,6 +2770,9 @@ static int ether_close(struct net_device *ndev) /* MAC deinit which inturn stop MAC Tx,Rx */ osi_hw_core_deinit(pdata->osi_core); + /* stop tx ts pending SKB workqueue and remove skb nodes */ + ether_flush_tx_ts_skb_list(pdata); + cancel_work_sync(&pdata->set_rx_mode_work); ether_stop_ivc(pdata); @@ -2801,7 +2925,7 @@ static int ether_tx_swcx_alloc(struct ether_priv_data *pdata, if (((tx_pkt_cx->flags & OSI_PKT_CX_VLAN) == OSI_PKT_CX_VLAN) || ((tx_pkt_cx->flags & OSI_PKT_CX_TSO) == OSI_PKT_CX_TSO) || (((tx_pkt_cx->flags & OSI_PKT_CX_PTP) == OSI_PKT_CX_PTP) && - /* Check only MGBE as we need ctx fro both sync mode */ + /* Check only MGBE as we need ctx for both sync mode */ ((pdata->osi_core->mac == OSI_MAC_HW_MGBE) || ((pdata->osi_dma->ptp_flag & OSI_PTP_SYNC_ONESTEP) == OSI_PTP_SYNC_ONESTEP)))) { @@ -5793,6 +5917,8 @@ static int ether_probe(struct platform_device *pdev) INIT_WORK(&pdata->set_rx_mode_work, set_rx_mode_work_func); osi_core->hw_feature = &pdata->hw_feat; INIT_LIST_HEAD(&pdata->mac_addr_list_head); + INIT_LIST_HEAD(&pdata->tx_ts_skb_head); + INIT_WORK(&pdata->tx_ts_work, ether_get_tx_ts); return 0; diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index b30f5a80..fd15e9d9 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -116,6 +116,12 @@ #define ETHER_EQOS_TX_CLK_1000M 125000000UL #define ETHER_EQOS_TX_CLK_100M 25000000UL #define ETHER_EQOS_TX_CLK_10M 2500000UL + +/** + * @brief 1 Second in Neno Second + */ +#define ETHER_ONESEC_NENOSEC 1000000000ULL + /** * @addtogroup CONFIG Ethernet configuration error codes * @@ -150,6 +156,11 @@ #define HW_HASH_TBL_SZ_0 0 /** @} */ +/** + * @brief Max pending SKB count + */ +#define ETHER_MAX_PENDING_SKB_CNT (64 * OSI_MGBE_MAX_NUM_CHANS) + /** * @brief Maximum buffer length per DMA descriptor (16KB). */ @@ -323,6 +334,20 @@ struct ether_mac_addr_list { unsigned int index; }; +/** + * @brief tx timestamp pending skb list node structure + */ +struct ether_tx_ts_skb_list { + /** Link list node head */ + struct list_head list_head; + /** if node is in use */ + unsigned int in_use; + /** skb pointer */ + struct sk_buff *skb; + /** packet id to identify timestamp */ + unsigned int pktid; +}; + /** * @brief Ethernet driver private data */ @@ -495,6 +520,12 @@ struct ether_priv_data { #endif /* MACSEC_SUPPORT */ /** local L2 filter address list head pointer */ struct list_head mac_addr_list_head; + /** skb tx timestamp update work queue */ + struct work_struct tx_ts_work; + /** local skb list head */ + struct list_head tx_ts_skb_head; + /** pre allocated memory for ether_tx_ts_skb_list list */ + struct ether_tx_ts_skb_list tx_ts_skb[ETHER_MAX_PENDING_SKB_CNT]; }; /** diff --git a/drivers/net/ethernet/nvidia/nvethernet/osd.c b/drivers/net/ethernet/nvidia/nvethernet/osd.c index b28369e6..aa9eb2b0 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/osd.c +++ b/drivers/net/ethernet/nvidia/nvethernet/osd.c @@ -376,6 +376,29 @@ void osd_receive_packet(void *priv, struct osi_rx_ring *rx_ring, ether_realloc_rx_skb(pdata, rx_ring, chan); } +/** + * @brief ether_get_free_tx_ts_node - get free node for pending SKB + * + * Algorithm: + * - Find index of statically allocayted free memory for pending SKB + * + * @param[in] pdata: OSD private data structure. + * + * @retval index number + */ +static inline unsigned int ether_get_free_tx_ts_node(struct ether_priv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < ETHER_MAX_PENDING_SKB_CNT; i++) { + if (pdata->tx_ts_skb[i].in_use == OSI_NONE) { + break; + } + } + + return i; +} + /** * @brief osd_transmit_complete - Transmit completion routine. * @@ -407,6 +430,7 @@ static void osd_transmit_complete(void *priv, void *buffer, unsigned long dmaadd struct osi_tx_ring *tx_ring; struct netdev_queue *txq; unsigned int chan, qinx; + unsigned int idx; ndev->stats.tx_bytes += len; @@ -445,8 +469,32 @@ static void osd_transmit_complete(void *priv, void *buffer, unsigned long dmaadd } ndev->stats.tx_packets++; - dev_consume_skb_any(skb); + if ((txdone_pkt_cx->flags & OSI_TXDONE_CX_TS_DELAYED) == + OSI_TXDONE_CX_TS_DELAYED) { + struct ether_tx_ts_skb_list *pnode = NULL; + + idx = ether_get_free_tx_ts_node(pdata); + if (idx == ETHER_MAX_PENDING_SKB_CNT) { + dev_dbg(pdata->dev, + "No free node to store pending SKB\n"); + dev_consume_skb_any(skb); + return; + } + + pnode = &pdata->tx_ts_skb[idx]; + pnode->skb = skb; + pnode->pktid = txdone_pkt_cx->pktid; + + dev_dbg(pdata->dev, "SKB %p added for pktid = %d\n", + skb, txdone_pkt_cx->pktid); + list_add_tail(&pnode->list_head, + &pdata->tx_ts_skb_head); + schedule_work(&pdata->tx_ts_work); + } else { + dev_consume_skb_any(skb); + } } + } void ether_assign_osd_ops(struct osi_core_priv_data *osi_core,