mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
nvethernet: Protect Tx timestamp list
add_skb_node() added to delete the old entries from the list but this can concurrently create the issue with ether_get_tx_ts() either from the workqueue or direct call from tx done routine. Protect the concurrent access of Tx timestamp list from add_skb_node() and ether_get_tx_ts(). Bug 3927990 Bug 4088361 Change-Id: Ifae84a072d7d16b259178f2a8e79c2065ce837d8 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2842411 (cherry picked from commit 8509d3060cc697cf43c3194521575f859de53e39) Signed-off-by: Rakesh Goyal <rgoyal@nvidia.com> Signed-off-by: Revanth Kumar Uppala <ruppala@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2895045 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
756347cd7f
commit
158ea57641
@@ -11,16 +11,41 @@
|
|||||||
#include <soc/tegra/fuse.h>
|
#include <soc/tegra/fuse.h>
|
||||||
#include <soc/tegra/virt/hv-ivc.h>
|
#include <soc/tegra/virt/hv-ivc.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ether_get_free_timestamp_node - get free node for timestmap info for SKB
|
||||||
|
*
|
||||||
|
* Algorithm:
|
||||||
|
* - Find index of statically allocated free memory for timestamp SKB
|
||||||
|
*
|
||||||
|
* @param[in] pdata: OSD private data structure.
|
||||||
|
*
|
||||||
|
* @retval index number
|
||||||
|
*/
|
||||||
|
static inline unsigned int ether_get_free_timestamp_node(struct ether_priv_data *pdata)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ETHER_MAX_PENDING_SKB_CNT; i++) {
|
||||||
|
if (pdata->timestamp_skb[i].in_use == OSI_NONE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
int ether_get_tx_ts(struct ether_priv_data *pdata)
|
int ether_get_tx_ts(struct ether_priv_data *pdata)
|
||||||
{
|
{
|
||||||
struct list_head *head_node, *temp_head_node;
|
struct list_head *head_node, *temp_head_node;
|
||||||
|
struct list_head *tstamp_head, *temp_tstamp_head;
|
||||||
struct skb_shared_hwtstamps shhwtstamp;
|
struct skb_shared_hwtstamps shhwtstamp;
|
||||||
struct osi_ioctl ioctl_data = {};
|
struct osi_ioctl ioctl_data = {};
|
||||||
unsigned long long nsec = 0x0;
|
unsigned long long nsec = 0x0;
|
||||||
struct ether_tx_ts_skb_list *pnode;
|
struct ether_tx_ts_skb_list *pnode;
|
||||||
|
struct ether_timestamp_skb_list *tnode;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool pending = false;
|
bool pending = false;
|
||||||
|
unsigned int idx = 0U;
|
||||||
|
|
||||||
if (!atomic_inc_and_test(&pdata->tx_ts_ref_cnt)) {
|
if (!atomic_inc_and_test(&pdata->tx_ts_ref_cnt)) {
|
||||||
/* Tx time stamp consumption already going on either from workq or func */
|
/* Tx time stamp consumption already going on either from workq or func */
|
||||||
@@ -32,13 +57,12 @@ int ether_get_tx_ts(struct ether_priv_data *pdata)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&pdata->txts_lock, flags);
|
||||||
list_for_each_safe(head_node, temp_head_node,
|
list_for_each_safe(head_node, temp_head_node,
|
||||||
&pdata->tx_ts_skb_head) {
|
&pdata->tx_ts_skb_head) {
|
||||||
pnode = list_entry(head_node,
|
pnode = list_entry(head_node,
|
||||||
struct ether_tx_ts_skb_list,
|
struct ether_tx_ts_skb_list,
|
||||||
list_head);
|
list_head);
|
||||||
memset(&shhwtstamp, 0,
|
|
||||||
sizeof(struct skb_shared_hwtstamps));
|
|
||||||
|
|
||||||
ioctl_data.cmd = OSI_CMD_GET_TX_TS;
|
ioctl_data.cmd = OSI_CMD_GET_TX_TS;
|
||||||
ioctl_data.tx_ts.pkt_id = pnode->pktid;
|
ioctl_data.tx_ts.pkt_id = pnode->pktid;
|
||||||
@@ -52,27 +76,36 @@ int ether_get_tx_ts(struct ether_priv_data *pdata)
|
|||||||
OSI_MAC_TCR_TXTSSMIS) {
|
OSI_MAC_TCR_TXTSSMIS) {
|
||||||
dev_warn(pdata->dev,
|
dev_warn(pdata->dev,
|
||||||
"No valid time for skb, removed\n");
|
"No valid time for skb, removed\n");
|
||||||
|
if (pnode->skb != NULL) {
|
||||||
|
dev_consume_skb_any(pnode->skb);
|
||||||
|
}
|
||||||
|
|
||||||
goto update_skb;
|
goto update_skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsec = ioctl_data.tx_ts.sec * ETHER_ONESEC_NENOSEC +
|
nsec = ioctl_data.tx_ts.sec * ETHER_ONESEC_NENOSEC +
|
||||||
ioctl_data.tx_ts.nsec;
|
ioctl_data.tx_ts.nsec;
|
||||||
|
|
||||||
/* pass tstamp to stack */
|
|
||||||
shhwtstamp.hwtstamp = ns_to_ktime(nsec);
|
|
||||||
if (pnode->skb != NULL) {
|
if (pnode->skb != NULL) {
|
||||||
skb_tstamp_tx(pnode->skb, &shhwtstamp);
|
idx = ether_get_free_timestamp_node(pdata);
|
||||||
|
if (idx < ETHER_MAX_PENDING_SKB_CNT) {
|
||||||
|
pdata->timestamp_skb[idx].in_use = OSI_ENABLE;
|
||||||
|
tnode = &pdata->timestamp_skb[idx];
|
||||||
|
tnode->skb = pnode->skb;
|
||||||
|
tnode->nsec = nsec;
|
||||||
|
dev_dbg(pdata->dev, "%s() SKB %p added for timestamp ns = %llu\n",
|
||||||
|
__func__, pnode->skb, nsec);
|
||||||
|
list_add_tail(&tnode->list_h,
|
||||||
|
&pdata->timestamp_skb_head);
|
||||||
|
} else {
|
||||||
|
dev_err(pdata->dev, "No free node to store timestamp for SKB\n");
|
||||||
|
dev_consume_skb_any(pnode->skb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update_skb:
|
update_skb:
|
||||||
if (pnode->skb != NULL) {
|
|
||||||
dev_consume_skb_any(pnode->skb);
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&pdata->txts_lock, flags);
|
|
||||||
list_del(head_node);
|
list_del(head_node);
|
||||||
pnode->in_use = OSI_DISABLE;
|
pnode->in_use = OSI_DISABLE;
|
||||||
raw_spin_unlock_irqrestore(&pdata->txts_lock, flags);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(pdata->dev, "Unable to retrieve TS from OSI\n");
|
dev_dbg(pdata->dev, "Unable to retrieve TS from OSI\n");
|
||||||
@@ -80,6 +113,25 @@ update_skb:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raw_spin_unlock_irqrestore(&pdata->txts_lock, flags);
|
||||||
|
|
||||||
|
list_for_each_safe(tstamp_head, temp_tstamp_head,
|
||||||
|
&pdata->timestamp_skb_head) {
|
||||||
|
tnode = list_entry(tstamp_head,
|
||||||
|
struct ether_timestamp_skb_list,
|
||||||
|
list_h);
|
||||||
|
if (tnode->in_use == OSI_ENABLE) {
|
||||||
|
memset(&shhwtstamp, 0,
|
||||||
|
sizeof(struct skb_shared_hwtstamps));
|
||||||
|
/* pass tstamp to stack */
|
||||||
|
shhwtstamp.hwtstamp = ns_to_ktime(tnode->nsec);
|
||||||
|
skb_tstamp_tx(tnode->skb, &shhwtstamp);
|
||||||
|
list_del(tstamp_head);
|
||||||
|
tnode->in_use = OSI_NONE;
|
||||||
|
}
|
||||||
|
dev_consume_skb_any(tnode->skb);
|
||||||
|
}
|
||||||
|
|
||||||
if (pending)
|
if (pending)
|
||||||
ret = -EAGAIN;
|
ret = -EAGAIN;
|
||||||
|
|
||||||
@@ -6599,6 +6651,7 @@ static int ether_probe(struct platform_device *pdev)
|
|||||||
INIT_DELAYED_WORK(&pdata->set_speed_work, set_speed_work_func);
|
INIT_DELAYED_WORK(&pdata->set_speed_work, set_speed_work_func);
|
||||||
osi_core->hw_feature = &pdata->hw_feat;
|
osi_core->hw_feature = &pdata->hw_feat;
|
||||||
INIT_LIST_HEAD(&pdata->tx_ts_skb_head);
|
INIT_LIST_HEAD(&pdata->tx_ts_skb_head);
|
||||||
|
INIT_LIST_HEAD(&pdata->timestamp_skb_head);
|
||||||
INIT_DELAYED_WORK(&pdata->tx_ts_work, ether_get_tx_ts_work);
|
INIT_DELAYED_WORK(&pdata->tx_ts_work, ether_get_tx_ts_work);
|
||||||
pdata->rx_m_enabled = false;
|
pdata->rx_m_enabled = false;
|
||||||
pdata->rx_pcs_m_enabled = false;
|
pdata->rx_pcs_m_enabled = false;
|
||||||
|
|||||||
@@ -385,6 +385,20 @@ struct ether_tx_ts_skb_list {
|
|||||||
unsigned long pkt_jiffies;
|
unsigned long pkt_jiffies;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief skb list with timestamp node structure
|
||||||
|
*/
|
||||||
|
struct ether_timestamp_skb_list {
|
||||||
|
/** Link list node head */
|
||||||
|
struct list_head list_h;
|
||||||
|
/** skb pointer */
|
||||||
|
struct sk_buff *skb;
|
||||||
|
/* Nano sec tx timestamp for skbinfo*/
|
||||||
|
unsigned long long nsec;
|
||||||
|
/** if node is in use */
|
||||||
|
unsigned int in_use;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ether_xtra_stat_counters - OSI core extra stat counters
|
* @brief ether_xtra_stat_counters - OSI core extra stat counters
|
||||||
*/
|
*/
|
||||||
@@ -578,8 +592,12 @@ struct ether_priv_data {
|
|||||||
struct delayed_work tx_ts_work;
|
struct delayed_work tx_ts_work;
|
||||||
/** local skb list head */
|
/** local skb list head */
|
||||||
struct list_head tx_ts_skb_head;
|
struct list_head tx_ts_skb_head;
|
||||||
|
/** local skb list head with timestamp*/
|
||||||
|
struct list_head timestamp_skb_head;
|
||||||
/** pre allocated memory for ether_tx_ts_skb_list list */
|
/** pre allocated memory for ether_tx_ts_skb_list list */
|
||||||
struct ether_tx_ts_skb_list tx_ts_skb[ETHER_MAX_PENDING_SKB_CNT];
|
struct ether_tx_ts_skb_list tx_ts_skb[ETHER_MAX_PENDING_SKB_CNT];
|
||||||
|
/** pre allocated memory for ether_timestamp_skb_list list */
|
||||||
|
struct ether_timestamp_skb_list timestamp_skb[ETHER_MAX_PENDING_SKB_CNT];
|
||||||
/** Atomic variable to hold the current pad calibration status */
|
/** Atomic variable to hold the current pad calibration status */
|
||||||
atomic_t padcal_in_progress;
|
atomic_t padcal_in_progress;
|
||||||
/** eqos dev pinctrl handle */
|
/** eqos dev pinctrl handle */
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ empty:
|
|||||||
raw_spin_lock_irqsave(&pdata->txts_lock, flags);
|
raw_spin_lock_irqsave(&pdata->txts_lock, flags);
|
||||||
idx = ether_get_free_tx_ts_node(pdata);
|
idx = ether_get_free_tx_ts_node(pdata);
|
||||||
if (idx == ETHER_MAX_PENDING_SKB_CNT) {
|
if (idx == ETHER_MAX_PENDING_SKB_CNT) {
|
||||||
dev_dbg(pdata->dev,
|
dev_err(pdata->dev,
|
||||||
"No free node to store pending SKB\n");
|
"No free node to store pending SKB\n");
|
||||||
dev_consume_skb_any(skb);
|
dev_consume_skb_any(skb);
|
||||||
raw_spin_unlock_irqrestore(&pdata->txts_lock, flags);
|
raw_spin_unlock_irqrestore(&pdata->txts_lock, flags);
|
||||||
|
|||||||
Reference in New Issue
Block a user