Skip to content

Commit

Permalink
sfc: Refactor link configuration
Browse files Browse the repository at this point in the history
Refactor PHY, MAC and NIC configuration operations so that the
existing link configuration can be re-pushed with:

	efx->phy_op->reconfigure(efx);
	efx->mac_op->reconfigure(efx);

and a new configuration with:

	efx->nic_op->reconfigure_port(efx);

(plus locking and error-checking).

We have not held the link settings in software (aside from flow
control), and have relied on asking the hardware what they are.  This
is a problem because in some cases the hardware may no longer be in a
state to tell us.  In particular, if an entire multi-port board is
reset through one port, the driver bindings to other ports have no
chance to save settings before recovering.

We only actually need to keep track of the autonegotiation settings,
so add an ethtool advertising mask to struct efx_nic, initialise it
in PHY init and update it as necessary.

Remove now-unneeded uses of efx_phy_op::{get,set}_settings() and
struct ethtool_cmd.

Much of this was done by Steve Hodgson <shodgson@solarflare.com>.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ben Hutchings authored and David S. Miller committed Nov 30, 2009
1 parent ef2b90e commit d3245b2
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 182 deletions.
132 changes: 80 additions & 52 deletions drivers/net/sfc/efx.c
Original file line number Diff line number Diff line change
Expand Up @@ -620,59 +620,84 @@ void efx_link_status_changed(struct efx_nic *efx)

}

void efx_link_set_advertising(struct efx_nic *efx, u32 advertising)
{
efx->link_advertising = advertising;
if (advertising) {
if (advertising & ADVERTISED_Pause)
efx->wanted_fc |= (EFX_FC_TX | EFX_FC_RX);
else
efx->wanted_fc &= ~(EFX_FC_TX | EFX_FC_RX);
if (advertising & ADVERTISED_Asym_Pause)
efx->wanted_fc ^= EFX_FC_TX;
}
}

void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type wanted_fc)
{
efx->wanted_fc = wanted_fc;
if (efx->link_advertising) {
if (wanted_fc & EFX_FC_RX)
efx->link_advertising |= (ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
else
efx->link_advertising &= ~(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
if (wanted_fc & EFX_FC_TX)
efx->link_advertising ^= ADVERTISED_Asym_Pause;
}
}

static void efx_fini_port(struct efx_nic *efx);

/* This call reinitialises the MAC to pick up new PHY settings. The
* caller must hold the mac_lock */
void __efx_reconfigure_port(struct efx_nic *efx)
/* Push loopback/power/transmit disable settings to the PHY, and reconfigure
* the MAC appropriately. All other PHY configuration changes are pushed
* through phy_op->set_settings(), and pushed asynchronously to the MAC
* through efx_monitor().
*
* Callers must hold the mac_lock
*/
int __efx_reconfigure_port(struct efx_nic *efx)
{
WARN_ON(!mutex_is_locked(&efx->mac_lock));
enum efx_phy_mode phy_mode;
int rc;

EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n",
raw_smp_processor_id());
WARN_ON(!mutex_is_locked(&efx->mac_lock));

/* Serialise the promiscuous flag with efx_set_multicast_list. */
if (efx_dev_registered(efx)) {
netif_addr_lock_bh(efx->net_dev);
netif_addr_unlock_bh(efx->net_dev);
}

efx->type->stop_stats(efx);
falcon_deconfigure_mac_wrapper(efx);

/* Reconfigure the PHY, disabling transmit in mac level loopback. */
/* Disable PHY transmit in mac level loopbacks */
phy_mode = efx->phy_mode;
if (LOOPBACK_INTERNAL(efx))
efx->phy_mode |= PHY_MODE_TX_DISABLED;
else
efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
efx->phy_op->reconfigure(efx);

if (falcon_switch_mac(efx))
goto fail;

efx->mac_op->reconfigure(efx);
rc = efx->type->reconfigure_port(efx);

efx->type->start_stats(efx);

/* Inform kernel of loss/gain of carrier */
efx_link_status_changed(efx);
return;
if (rc)
efx->phy_mode = phy_mode;

fail:
EFX_ERR(efx, "failed to reconfigure MAC\n");
efx->port_enabled = false;
efx_fini_port(efx);
return rc;
}

/* Reinitialise the MAC to pick up new PHY settings, even if the port is
* disabled. */
void efx_reconfigure_port(struct efx_nic *efx)
int efx_reconfigure_port(struct efx_nic *efx)
{
int rc;

EFX_ASSERT_RESET_SERIALISED(efx);

mutex_lock(&efx->mac_lock);
__efx_reconfigure_port(efx);
rc = __efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);

return rc;
}

/* Asynchronous work item for changing MAC promiscuity and multicast
Expand Down Expand Up @@ -737,14 +762,18 @@ static int efx_init_port(struct efx_nic *efx)
rc = efx->phy_op->init(efx);
if (rc)
goto fail1;
efx->phy_op->reconfigure(efx);
rc = falcon_switch_mac(efx);
if (rc)
goto fail2;
efx->mac_op->reconfigure(efx);

efx->port_initialized = true;

/* Reconfigure the MAC before creating dma queues (required for
* Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */
efx->mac_op->reconfigure(efx);

/* Ensure the PHY advertises the correct flow control settings */
rc = efx->phy_op->reconfigure(efx);
if (rc)
goto fail2;

mutex_unlock(&efx->mac_lock);
return 0;

Expand Down Expand Up @@ -1209,12 +1238,6 @@ static void efx_stop_all(struct efx_nic *efx)
/* Flush efx_mac_work(), refill_workqueue, monitor_work */
efx_flush_all(efx);

/* Isolate the MAC from the TX and RX engines, so that queue
* flushes will complete in a timely fashion. */
falcon_deconfigure_mac_wrapper(efx);
msleep(10); /* Let the Rx FIFO drain */
falcon_drain_tx_fifo(efx);

/* Stop the kernel transmit interface late, so the watchdog
* timer isn't ticking over the flush */
if (efx_dev_registered(efx)) {
Expand Down Expand Up @@ -1491,7 +1514,14 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu)
EFX_LOG(efx, "changing MTU to %d\n", new_mtu);

efx_fini_channels(efx);

mutex_lock(&efx->mac_lock);
/* Reconfigure the MAC before enabling the dma queues so that
* the RX buffers don't overflow */
net_dev->mtu = new_mtu;
efx->mac_op->reconfigure(efx);
mutex_unlock(&efx->mac_lock);

efx_init_channels(efx);

efx_start_all(efx);
Expand All @@ -1515,7 +1545,9 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data)
memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len);

/* Reconfigure the MAC */
efx_reconfigure_port(efx);
mutex_lock(&efx->mac_lock);
efx->mac_op->reconfigure(efx);
mutex_unlock(&efx->mac_lock);

return 0;
}
Expand Down Expand Up @@ -1682,17 +1714,14 @@ static void efx_unregister_netdev(struct efx_nic *efx)

/* Tears down the entire software state and most of the hardware state
* before reset. */
void efx_reset_down(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd)
void efx_reset_down(struct efx_nic *efx, enum reset_type method)
{
EFX_ASSERT_RESET_SERIALISED(efx);

efx_stop_all(efx);
mutex_lock(&efx->mac_lock);
mutex_lock(&efx->spi_lock);

efx->phy_op->get_settings(efx, ecmd);

efx_fini_channels(efx);
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE)
efx->phy_op->fini(efx);
Expand All @@ -1704,8 +1733,7 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method,
* that we were unable to reinitialise the hardware, and the
* driver should be disabled. If ok is false, then the rx and tx
* engines are not restarted, pending a RESET_DISABLE. */
int efx_reset_up(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd, bool ok)
int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
{
int rc;

Expand All @@ -1722,16 +1750,17 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method,
rc = efx->phy_op->init(efx);
if (rc)
ok = false;
if (efx->phy_op->reconfigure(efx))
EFX_ERR(efx, "could not restore PHY settings\n");
}
if (!ok)
efx->port_initialized = false;
}

if (ok) {
efx_init_channels(efx);
efx->mac_op->reconfigure(efx);

if (efx->phy_op->set_settings(efx, ecmd))
EFX_ERR(efx, "could not restore PHY settings\n");
efx_init_channels(efx);
}

mutex_unlock(&efx->spi_lock);
Expand All @@ -1753,7 +1782,6 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method,
*/
static int efx_reset(struct efx_nic *efx)
{
struct ethtool_cmd ecmd;
enum reset_type method = efx->reset_pending;
int rc = 0;

Expand All @@ -1769,7 +1797,7 @@ static int efx_reset(struct efx_nic *efx)

EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method));

efx_reset_down(efx, method, &ecmd);
efx_reset_down(efx, method);

rc = efx->type->reset(efx, method);
if (rc) {
Expand All @@ -1788,10 +1816,10 @@ static int efx_reset(struct efx_nic *efx)

/* Leave device stopped if necessary */
if (method == RESET_TYPE_DISABLE) {
efx_reset_up(efx, method, &ecmd, false);
efx_reset_up(efx, method, false);
rc = -EIO;
} else {
rc = efx_reset_up(efx, method, &ecmd, true);
rc = efx_reset_up(efx, method, true);
}

out_disable:
Expand Down Expand Up @@ -1895,7 +1923,7 @@ bool efx_port_dummy_op_poll(struct efx_nic *efx)

static struct efx_phy_operations efx_dummy_phy_operations = {
.init = efx_port_dummy_op_int,
.reconfigure = efx_port_dummy_op_void,
.reconfigure = efx_port_dummy_op_int,
.poll = efx_port_dummy_op_poll,
.fini = efx_port_dummy_op_void,
};
Expand Down
12 changes: 6 additions & 6 deletions drivers/net/sfc/efx.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ extern void efx_process_channel_now(struct efx_channel *channel);
#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1)

/* Ports */
extern void efx_reconfigure_port(struct efx_nic *efx);
extern void __efx_reconfigure_port(struct efx_nic *efx);
extern int efx_reconfigure_port(struct efx_nic *efx);
extern int __efx_reconfigure_port(struct efx_nic *efx);

/* Ethtool support */
extern int efx_ethtool_get_settings(struct net_device *net_dev,
Expand All @@ -71,10 +71,8 @@ extern int efx_ethtool_set_settings(struct net_device *net_dev,
extern const struct ethtool_ops efx_ethtool_ops;

/* Reset handling */
extern void efx_reset_down(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd);
extern int efx_reset_up(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd, bool ok);
extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);

/* Global */
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
Expand Down Expand Up @@ -115,5 +113,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel)
}

extern void efx_link_status_changed(struct efx_nic *efx);
extern void efx_link_set_advertising(struct efx_nic *efx, u32);
extern void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type);

#endif /* EFX_EFX_H */
Loading

0 comments on commit d3245b2

Please sign in to comment.