Skip to content

Commit

Permalink
net: stmmac: Rework and fix TX Timeout code
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jose Abreu authored and David S. Miller committed Mar 30, 2018
1 parent 02281a3 commit 34877a1
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
11 changes: 11 additions & 0 deletions drivers/net/ethernet/stmicro/stmmac/stmmac.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
67 changes: 62 additions & 5 deletions drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -4342,6 +4396,8 @@ int stmmac_dvr_probe(struct device *device,
netif_napi_del(&rx_q->napi);
}
error_hw_init:
destroy_workqueue(priv->wq);
error_wq:
free_netdev(ndev);

return ret;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 34877a1

Please sign in to comment.