mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
net: stmmac: Rework and fix TX Timeout code
Currently TX Timeout handler does not behaves as expected and leads to an unrecoverable state. Rework current implementation of TX Timeout handling to actually perform a complete reset of the driver state and IP. We use deferred work to init a task which will be responsible for resetting the system. Signed-off-by: Jose Abreu <joabreu@synopsys.com> Cc: David S. Miller <davem@davemloft.net> Cc: Joao Pinto <jpinto@synopsys.com> Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
02281a3525
commit
34877a15f7
@ -145,6 +145,17 @@ struct stmmac_priv {
|
||||
struct dentry *dbgfs_rings_status;
|
||||
struct dentry *dbgfs_dma_cap;
|
||||
#endif
|
||||
|
||||
unsigned long state;
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct service_task;
|
||||
};
|
||||
|
||||
enum stmmac_state {
|
||||
STMMAC_DOWN,
|
||||
STMMAC_RESET_REQUESTED,
|
||||
STMMAC_RESETING,
|
||||
STMMAC_SERVICE_SCHED,
|
||||
};
|
||||
|
||||
int stmmac_mdio_unregister(struct net_device *ndev);
|
||||
|
@ -196,6 +196,20 @@ static void stmmac_start_all_queues(struct stmmac_priv *priv)
|
||||
netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue));
|
||||
}
|
||||
|
||||
static void stmmac_service_event_schedule(struct stmmac_priv *priv)
|
||||
{
|
||||
if (!test_bit(STMMAC_DOWN, &priv->state) &&
|
||||
!test_and_set_bit(STMMAC_SERVICE_SCHED, &priv->state))
|
||||
queue_work(priv->wq, &priv->service_task);
|
||||
}
|
||||
|
||||
static void stmmac_global_err(struct stmmac_priv *priv)
|
||||
{
|
||||
netif_carrier_off(priv->dev);
|
||||
set_bit(STMMAC_RESET_REQUESTED, &priv->state);
|
||||
stmmac_service_event_schedule(priv);
|
||||
}
|
||||
|
||||
/**
|
||||
* stmmac_clk_csr_set - dynamically set the MDC clock
|
||||
* @priv: driver private structure
|
||||
@ -3587,12 +3601,8 @@ static int stmmac_poll(struct napi_struct *napi, int budget)
|
||||
static void stmmac_tx_timeout(struct net_device *dev)
|
||||
{
|
||||
struct stmmac_priv *priv = netdev_priv(dev);
|
||||
u32 tx_count = priv->plat->tx_queues_to_use;
|
||||
u32 chan;
|
||||
|
||||
/* Clear Tx resources and restart transmitting again */
|
||||
for (chan = 0; chan < tx_count; chan++)
|
||||
stmmac_tx_err(priv, chan);
|
||||
stmmac_global_err(priv);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3716,6 +3726,10 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Check if adapter is up */
|
||||
if (test_bit(STMMAC_DOWN, &priv->state))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* To handle GMAC own interrupts */
|
||||
if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
|
||||
int status = priv->hw->mac->host_irq_status(priv->hw,
|
||||
@ -4051,6 +4065,37 @@ static const struct net_device_ops stmmac_netdev_ops = {
|
||||
.ndo_set_mac_address = stmmac_set_mac_address,
|
||||
};
|
||||
|
||||
static void stmmac_reset_subtask(struct stmmac_priv *priv)
|
||||
{
|
||||
if (!test_and_clear_bit(STMMAC_RESET_REQUESTED, &priv->state))
|
||||
return;
|
||||
if (test_bit(STMMAC_DOWN, &priv->state))
|
||||
return;
|
||||
|
||||
netdev_err(priv->dev, "Reset adapter.\n");
|
||||
|
||||
rtnl_lock();
|
||||
netif_trans_update(priv->dev);
|
||||
while (test_and_set_bit(STMMAC_RESETING, &priv->state))
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
set_bit(STMMAC_DOWN, &priv->state);
|
||||
dev_close(priv->dev);
|
||||
dev_open(priv->dev);
|
||||
clear_bit(STMMAC_DOWN, &priv->state);
|
||||
clear_bit(STMMAC_RESETING, &priv->state);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static void stmmac_service_task(struct work_struct *work)
|
||||
{
|
||||
struct stmmac_priv *priv = container_of(work, struct stmmac_priv,
|
||||
service_task);
|
||||
|
||||
stmmac_reset_subtask(priv);
|
||||
clear_bit(STMMAC_SERVICE_SCHED, &priv->state);
|
||||
}
|
||||
|
||||
/**
|
||||
* stmmac_hw_init - Init the MAC device
|
||||
* @priv: driver private structure
|
||||
@ -4212,6 +4257,15 @@ int stmmac_dvr_probe(struct device *device,
|
||||
/* Verify driver arguments */
|
||||
stmmac_verify_args();
|
||||
|
||||
/* Allocate workqueue */
|
||||
priv->wq = create_singlethread_workqueue("stmmac_wq");
|
||||
if (!priv->wq) {
|
||||
dev_err(priv->device, "failed to create workqueue\n");
|
||||
goto error_wq;
|
||||
}
|
||||
|
||||
INIT_WORK(&priv->service_task, stmmac_service_task);
|
||||
|
||||
/* Override with kernel parameters if supplied XXX CRS XXX
|
||||
* this needs to have multiple instances
|
||||
*/
|
||||
@ -4342,6 +4396,8 @@ error_mdio_register:
|
||||
netif_napi_del(&rx_q->napi);
|
||||
}
|
||||
error_hw_init:
|
||||
destroy_workqueue(priv->wq);
|
||||
error_wq:
|
||||
free_netdev(ndev);
|
||||
|
||||
return ret;
|
||||
@ -4374,6 +4430,7 @@ int stmmac_dvr_remove(struct device *dev)
|
||||
priv->hw->pcs != STMMAC_PCS_TBI &&
|
||||
priv->hw->pcs != STMMAC_PCS_RTBI)
|
||||
stmmac_mdio_unregister(ndev);
|
||||
destroy_workqueue(priv->wq);
|
||||
free_netdev(ndev);
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user