Skip to content

Commit

Permalink
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/bwh/sfc-next
  • Loading branch information
David S. Miller committed Jul 14, 2011
2 parents cd0d722 + ff79c8a commit 10d1e8c
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 99 deletions.
7 changes: 3 additions & 4 deletions drivers/net/sfc/Kconfig
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
config SFC
tristate "Solarflare Solarstorm SFC4000/SFC9000-family support"
tristate "Solarflare SFC4000/SFC9000-family support"
depends on PCI && INET
select MDIO
select CRC32
select I2C
select I2C_ALGOBIT
help
This driver supports 10-gigabit Ethernet cards based on
the Solarflare Communications Solarstorm SFC4000 and
SFC9000-family controllers.
the Solarflare SFC4000 and SFC9000-family controllers.

To compile this driver as a module, choose M here. The module
will be called sfc.
config SFC_MTD
bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support"
bool "Solarflare SFC4000/SFC9000-family MTD support"
depends on SFC && MTD && !(SFC=y && MTD=m)
default y
help
Expand Down
60 changes: 23 additions & 37 deletions drivers/net/sfc/efx.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ static int efx_process_channel(struct efx_channel *channel, int budget)
struct efx_nic *efx = channel->efx;
int spent;

if (unlikely(efx->reset_pending != RESET_TYPE_NONE ||
!channel->enabled))
if (unlikely(efx->reset_pending || !channel->enabled))
return 0;

spent = efx_nic_process_eventq(channel, budget);
Expand Down Expand Up @@ -1461,7 +1460,7 @@ static void efx_start_all(struct efx_nic *efx)
* reset_pending [modified from an atomic context], we instead guarantee
* that efx_mcdi_mode_poll() isn't reverted erroneously */
efx_mcdi_mode_event(efx);
if (efx->reset_pending != RESET_TYPE_NONE)
if (efx->reset_pending)
efx_mcdi_mode_poll(efx);

/* Start the hardware monitor if there is one. Otherwise (we're link
Expand Down Expand Up @@ -2118,8 +2117,10 @@ int efx_reset(struct efx_nic *efx, enum reset_type method)
goto out;
}

/* Allow resets to be rescheduled. */
efx->reset_pending = RESET_TYPE_NONE;
/* Clear flags for the scopes we covered. We assume the NIC and
* driver are now quiescent so that there is no race here.
*/
efx->reset_pending &= -(1 << (method + 1));

/* Reinitialise bus-mastering, which may have been turned off before
* the reset was scheduled. This is still appropriate, even in the
Expand Down Expand Up @@ -2154,61 +2155,46 @@ int efx_reset(struct efx_nic *efx, enum reset_type method)
static void efx_reset_work(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic, reset_work);
unsigned long pending = ACCESS_ONCE(efx->reset_pending);

if (efx->reset_pending == RESET_TYPE_NONE)
if (!pending)
return;

/* If we're not RUNNING then don't reset. Leave the reset_pending
* flag set so that efx_pci_probe_main will be retried */
* flags set so that efx_pci_probe_main will be retried */
if (efx->state != STATE_RUNNING) {
netif_info(efx, drv, efx->net_dev,
"scheduled reset quenched. NIC not RUNNING\n");
return;
}

rtnl_lock();
(void)efx_reset(efx, efx->reset_pending);
(void)efx_reset(efx, fls(pending) - 1);
rtnl_unlock();
}

void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
{
enum reset_type method;

if (efx->reset_pending != RESET_TYPE_NONE) {
netif_info(efx, drv, efx->net_dev,
"quenching already scheduled reset\n");
return;
}

switch (type) {
case RESET_TYPE_INVISIBLE:
case RESET_TYPE_ALL:
case RESET_TYPE_WORLD:
case RESET_TYPE_DISABLE:
method = type;
netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n",
RESET_TYPE(method));
break;
case RESET_TYPE_RX_RECOVERY:
case RESET_TYPE_RX_DESC_FETCH:
case RESET_TYPE_TX_DESC_FETCH:
case RESET_TYPE_TX_SKIP:
method = RESET_TYPE_INVISIBLE;
break;
case RESET_TYPE_MC_FAILURE:
default:
method = RESET_TYPE_ALL;
break;
}

if (method != type)
method = efx->type->map_reset_reason(type);
netif_dbg(efx, drv, efx->net_dev,
"scheduling %s reset for %s\n",
RESET_TYPE(method), RESET_TYPE(type));
else
netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n",
RESET_TYPE(method));
break;
}

efx->reset_pending = method;
set_bit(method, &efx->reset_pending);

/* efx_process_channel() will no longer read events once a
* reset is scheduled. So switch back to poll'd MCDI completions. */
Expand Down Expand Up @@ -2288,7 +2274,6 @@ static int efx_init_struct(struct efx_nic *efx, const struct efx_nic_type *type,
efx->pci_dev = pci_dev;
efx->msg_enable = debug;
efx->state = STATE_INIT;
efx->reset_pending = RESET_TYPE_NONE;
strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name));

efx->net_dev = net_dev;
Expand Down Expand Up @@ -2491,7 +2476,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
goto fail1;

netif_info(efx, probe, efx->net_dev,
"Solarflare Communications NIC detected\n");
"Solarflare NIC detected\n");

/* Set up basic I/O (BAR mappings etc) */
rc = efx_init_io(efx);
Expand All @@ -2510,7 +2495,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
cancel_work_sync(&efx->reset_work);

if (rc == 0) {
if (efx->reset_pending != RESET_TYPE_NONE) {
if (efx->reset_pending) {
/* If there was a scheduled reset during
* probe, the NIC is probably hosed anyway */
efx_pci_remove_main(efx);
Expand All @@ -2521,11 +2506,12 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
}

/* Retry if a recoverably reset event has been scheduled */
if ((efx->reset_pending != RESET_TYPE_INVISIBLE) &&
(efx->reset_pending != RESET_TYPE_ALL))
if (efx->reset_pending &
~(1 << RESET_TYPE_INVISIBLE | 1 << RESET_TYPE_ALL) ||
!efx->reset_pending)
goto fail3;

efx->reset_pending = RESET_TYPE_NONE;
efx->reset_pending = 0;
}

if (rc) {
Expand Down Expand Up @@ -2609,7 +2595,7 @@ static int efx_pm_poweroff(struct device *dev)

efx->type->fini(efx);

efx->reset_pending = RESET_TYPE_NONE;
efx->reset_pending = 0;

pci_save_state(pci_dev);
return pci_set_power_state(pci_dev, PCI_D3hot);
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/sfc/enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ enum efx_loopback_mode {
* other valuesspecify reasons, which efx_schedule_reset() will choose
* a method for.
*
* Reset methods are numbered in order of increasing scope.
*
* @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts
* @RESET_TYPE_ALL: reset everything but PCI core blocks
* @RESET_TYPE_WORLD: reset everything, save & restore PCI config
Expand All @@ -147,7 +149,6 @@ enum efx_loopback_mode {
* @RESET_TYPE_MC_FAILURE: MC reboot/assertion
*/
enum reset_type {
RESET_TYPE_NONE = -1,
RESET_TYPE_INVISIBLE = 0,
RESET_TYPE_ALL = 1,
RESET_TYPE_WORLD = 2,
Expand Down
27 changes: 5 additions & 22 deletions drivers/net/sfc/ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -796,30 +796,13 @@ static int efx_ethtool_set_wol(struct net_device *net_dev,
static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
{
struct efx_nic *efx = netdev_priv(net_dev);
enum reset_type method;
enum {
ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER |
ETH_RESET_OFFLOAD | ETH_RESET_MAC)
};

/* Check for minimal reset flags */
if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE)
return -EINVAL;
*flags ^= ETH_RESET_EFX_INVISIBLE;
method = RESET_TYPE_INVISIBLE;

if (*flags & ETH_RESET_PHY) {
*flags ^= ETH_RESET_PHY;
method = RESET_TYPE_ALL;
}
int rc;

if ((*flags & efx->type->reset_world_flags) ==
efx->type->reset_world_flags) {
*flags ^= efx->type->reset_world_flags;
method = RESET_TYPE_WORLD;
}
rc = efx->type->map_reset_flags(flags);
if (rc < 0)
return rc;

return efx_reset(efx, method);
return efx_reset(efx, rc);
}

static int
Expand Down
51 changes: 48 additions & 3 deletions drivers/net/sfc/falcon.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
efx_oword_t reg;
int link_speed, isolate;

isolate = (efx->reset_pending != RESET_TYPE_NONE);
isolate = !!ACCESS_ONCE(efx->reset_pending);

switch (link_state->speed) {
case 10000: link_speed = 3; break;
Expand Down Expand Up @@ -1051,6 +1051,49 @@ static int falcon_b0_test_registers(struct efx_nic *efx)
**************************************************************************
*/

static enum reset_type falcon_map_reset_reason(enum reset_type reason)
{
switch (reason) {
case RESET_TYPE_RX_RECOVERY:
case RESET_TYPE_RX_DESC_FETCH:
case RESET_TYPE_TX_DESC_FETCH:
case RESET_TYPE_TX_SKIP:
/* These can occasionally occur due to hardware bugs.
* We try to reset without disrupting the link.
*/
return RESET_TYPE_INVISIBLE;
default:
return RESET_TYPE_ALL;
}
}

static int falcon_map_reset_flags(u32 *flags)
{
enum {
FALCON_RESET_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER |
ETH_RESET_OFFLOAD | ETH_RESET_MAC),
FALCON_RESET_ALL = FALCON_RESET_INVISIBLE | ETH_RESET_PHY,
FALCON_RESET_WORLD = FALCON_RESET_ALL | ETH_RESET_IRQ,
};

if ((*flags & FALCON_RESET_WORLD) == FALCON_RESET_WORLD) {
*flags &= ~FALCON_RESET_WORLD;
return RESET_TYPE_WORLD;
}

if ((*flags & FALCON_RESET_ALL) == FALCON_RESET_ALL) {
*flags &= ~FALCON_RESET_ALL;
return RESET_TYPE_ALL;
}

if ((*flags & FALCON_RESET_INVISIBLE) == FALCON_RESET_INVISIBLE) {
*flags &= ~FALCON_RESET_INVISIBLE;
return RESET_TYPE_INVISIBLE;
}

return -EINVAL;
}

/* Resets NIC to known state. This routine must be called in process
* context and is allowed to sleep. */
static int __falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
Expand Down Expand Up @@ -1709,6 +1752,8 @@ const struct efx_nic_type falcon_a1_nic_type = {
.init = falcon_init_nic,
.fini = efx_port_dummy_op_void,
.monitor = falcon_monitor,
.map_reset_reason = falcon_map_reset_reason,
.map_reset_flags = falcon_map_reset_flags,
.reset = falcon_reset_hw,
.probe_port = falcon_probe_port,
.remove_port = falcon_remove_port,
Expand Down Expand Up @@ -1741,7 +1786,6 @@ const struct efx_nic_type falcon_a1_nic_type = {
.tx_dc_base = 0x130000,
.rx_dc_base = 0x100000,
.offload_features = NETIF_F_IP_CSUM,
.reset_world_flags = ETH_RESET_IRQ,
};

const struct efx_nic_type falcon_b0_nic_type = {
Expand All @@ -1750,6 +1794,8 @@ const struct efx_nic_type falcon_b0_nic_type = {
.init = falcon_init_nic,
.fini = efx_port_dummy_op_void,
.monitor = falcon_monitor,
.map_reset_reason = falcon_map_reset_reason,
.map_reset_flags = falcon_map_reset_flags,
.reset = falcon_reset_hw,
.probe_port = falcon_probe_port,
.remove_port = falcon_remove_port,
Expand Down Expand Up @@ -1791,6 +1837,5 @@ const struct efx_nic_type falcon_b0_nic_type = {
.tx_dc_base = 0x130000,
.rx_dc_base = 0x100000,
.offload_features = NETIF_F_IP_CSUM | NETIF_F_RXHASH | NETIF_F_NTUPLE,
.reset_world_flags = ETH_RESET_IRQ,
};

45 changes: 26 additions & 19 deletions drivers/net/sfc/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,28 +335,35 @@ static int efx_filter_search(struct efx_filter_table *table,
bool for_insert, int *depth_required)
{
unsigned hash, incr, filter_idx, depth, depth_max;
struct efx_filter_spec *cmp;

hash = efx_filter_hash(key);
incr = efx_filter_increment(key);
depth_max = (spec->priority <= EFX_FILTER_PRI_HINT ?
FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX);

for (depth = 1, filter_idx = hash & (table->size - 1);
depth <= depth_max && test_bit(filter_idx, table->used_bitmap);
++depth) {
cmp = &table->spec[filter_idx];
if (efx_filter_equal(spec, cmp))
goto found;

filter_idx = hash & (table->size - 1);
depth = 1;
depth_max = (for_insert ?
(spec->priority <= EFX_FILTER_PRI_HINT ?
FILTER_CTL_SRCH_HINT_MAX : FILTER_CTL_SRCH_MAX) :
table->search_depth[spec->type]);

for (;;) {
/* Return success if entry is used and matches this spec
* or entry is unused and we are trying to insert.
*/
if (test_bit(filter_idx, table->used_bitmap) ?
efx_filter_equal(spec, &table->spec[filter_idx]) :
for_insert) {
*depth_required = depth;
return filter_idx;
}

/* Return failure if we reached the maximum search depth */
if (depth == depth_max)
return for_insert ? -EBUSY : -ENOENT;

filter_idx = (filter_idx + incr) & (table->size - 1);
++depth;
}
if (!for_insert)
return -ENOENT;
if (depth > depth_max)
return -EBUSY;
found:
*depth_required = depth;
return filter_idx;
}

/* Construct/deconstruct external filter IDs */
Expand Down Expand Up @@ -650,11 +657,11 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
return -EPROTONOSUPPORT;

/* RFS must validate the IP header length before calling us */
EFX_BUG_ON_PARANOID(!pskb_may_pull(skb, nhoff + sizeof(*ip)));
EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
ip = (const struct iphdr *)(skb->data + nhoff);
if (ip_is_fragment(ip))
return -EPROTONOSUPPORT;
EFX_BUG_ON_PARANOID(!pskb_may_pull(skb, nhoff + 4 * ip->ihl + 4));
EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);

efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, 0, rxq_index);
Expand Down
Loading

0 comments on commit 10d1e8c

Please sign in to comment.