Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (73 commits)
  net: Fix typo in net/core/sock.c.
  ppp: Do not free not yet unregistered net device.
  netfilter: xt_iprange: module aliases for xt_iprange
  netfilter: ctnetlink: dump conntrack ID in event messages
  irda: Fix a misalign access issue. (v2)
  sctp: Fix use of uninitialized pointer
  cipso: Relax too much careful cipso hash function.
  tcp FRTO: work-around inorder receivers
  tcp FRTO: Fix fallback to conventional recovery
  New maintainer for Intel ethernet adapters
  DM9000: Use delayed work to update MII PHY state
  DM9000: Update and fix driver debugging messages
  DM9000: Add __devinit and __devexit attributes to probe and remove
  sky2: fix simple define thinko
  [netdrvr] sfc: sfc: Add self-test support
  [netdrvr] sfc: Increment rx_reset when reported as driver event
  [netdrvr] sfc: Remove unused macro EFX_XAUI_RETRAIN_MAX
  [netdrvr] sfc: Fix code formatting
  [netdrvr] sfc: Remove kernel-doc comments for removed members of struct efx_nic
  [netdrvr] sfc: Remove garbage from comment
  ...
This commit is contained in:
Linus Torvalds 2008-05-14 10:08:24 -07:00
commit 6aa5fc4349
94 changed files with 3378 additions and 734 deletions

View File

@ -53,11 +53,13 @@ int register_memory_notifier(struct notifier_block *nb)
{ {
return blocking_notifier_chain_register(&memory_chain, nb); return blocking_notifier_chain_register(&memory_chain, nb);
} }
EXPORT_SYMBOL(register_memory_notifier);
void unregister_memory_notifier(struct notifier_block *nb) void unregister_memory_notifier(struct notifier_block *nb)
{ {
blocking_notifier_chain_unregister(&memory_chain, nb); blocking_notifier_chain_unregister(&memory_chain, nb);
} }
EXPORT_SYMBOL(unregister_memory_notifier);
/* /*
* register_memory - Setup a sysfs device for a memory block * register_memory - Setup a sysfs device for a memory block

View File

@ -2426,7 +2426,7 @@ config CHELSIO_T3
config EHEA config EHEA
tristate "eHEA Ethernet support" tristate "eHEA Ethernet support"
depends on IBMEBUS && INET && SPARSEMEM depends on IBMEBUS && INET && SPARSEMEM && MEMORY_HOTPLUG
select INET_LRO select INET_LRO
---help--- ---help---
This driver supports the IBM pSeries eHEA ethernet adapter. This driver supports the IBM pSeries eHEA ethernet adapter.

View File

@ -1,7 +1,7 @@
/* /*
* Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
* Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com> * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
* Copyright(c) 2006 Jay Cliburn <jcliburn@gmail.com> * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
* *
* Derived from Intel e1000 driver * Derived from Intel e1000 driver
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
@ -36,7 +36,6 @@
* A very incomplete list of things that need to be dealt with: * A very incomplete list of things that need to be dealt with:
* *
* TODO: * TODO:
* Wake on LAN.
* Add more ethtool functions. * Add more ethtool functions.
* Fix abstruse irq enable/disable condition described here: * Fix abstruse irq enable/disable condition described here:
* http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2 * http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2
@ -638,21 +637,18 @@ static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw)
} }
/* /*
*TODO: do something or get rid of this * Force the PHY into power saving mode using vendor magic.
*/ */
#ifdef CONFIG_PM #ifdef CONFIG_PM
static s32 atl1_phy_enter_power_saving(struct atl1_hw *hw) static void atl1_phy_enter_power_saving(struct atl1_hw *hw)
{ {
/* s32 ret_val; atl1_write_phy_reg(hw, MII_DBG_ADDR, 0);
* u16 phy_data; atl1_write_phy_reg(hw, MII_DBG_DATA, 0x124E);
*/ atl1_write_phy_reg(hw, MII_DBG_ADDR, 2);
atl1_write_phy_reg(hw, MII_DBG_DATA, 0x3000);
atl1_write_phy_reg(hw, MII_DBG_ADDR, 3);
atl1_write_phy_reg(hw, MII_DBG_DATA, 0);
/*
ret_val = atl1_write_phy_reg(hw, ...);
ret_val = atl1_write_phy_reg(hw, ...);
....
*/
return 0;
} }
#endif #endif
@ -2784,64 +2780,93 @@ static int atl1_suspend(struct pci_dev *pdev, pm_message_t state)
struct atl1_hw *hw = &adapter->hw; struct atl1_hw *hw = &adapter->hw;
u32 ctrl = 0; u32 ctrl = 0;
u32 wufc = adapter->wol; u32 wufc = adapter->wol;
u32 val;
int retval;
u16 speed;
u16 duplex;
netif_device_detach(netdev); netif_device_detach(netdev);
if (netif_running(netdev)) if (netif_running(netdev))
atl1_down(adapter); atl1_down(adapter);
retval = pci_save_state(pdev);
if (retval)
return retval;
atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl);
atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl);
if (ctrl & BMSR_LSTATUS) val = ctrl & BMSR_LSTATUS;
if (val)
wufc &= ~ATLX_WUFC_LNKC; wufc &= ~ATLX_WUFC_LNKC;
/* reduce speed to 10/100M */ if (val && wufc) {
if (wufc) { val = atl1_get_speed_and_duplex(hw, &speed, &duplex);
atl1_phy_enter_power_saving(hw); if (val) {
/* if resume, let driver to re- setup link */ if (netif_msg_ifdown(adapter))
hw->phy_configured = false; dev_printk(KERN_DEBUG, &pdev->dev,
atl1_set_mac_addr(hw); "error getting speed/duplex\n");
atlx_set_multi(netdev); goto disable_wol;
}
ctrl = 0; ctrl = 0;
/* turn on magic packet wol */
/* enable magic packet WOL */
if (wufc & ATLX_WUFC_MAG) if (wufc & ATLX_WUFC_MAG)
ctrl = WOL_MAGIC_EN | WOL_MAGIC_PME_EN; ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN);
/* turn on Link change WOL */
if (wufc & ATLX_WUFC_LNKC)
ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL); iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL);
ioread32(hw->hw_addr + REG_WOL_CTRL);
/* turn on all-multi mode if wake on multicast is enabled */ /* configure the mac */
ctrl = ioread32(hw->hw_addr + REG_MAC_CTRL); ctrl = MAC_CTRL_RX_EN;
ctrl &= ~MAC_CTRL_DBG; ctrl |= ((u32)((speed == SPEED_1000) ? MAC_CTRL_SPEED_1000 :
ctrl &= ~MAC_CTRL_PROMIS_EN; MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT);
if (wufc & ATLX_WUFC_MC) if (duplex == FULL_DUPLEX)
ctrl |= MAC_CTRL_MC_ALL_EN; ctrl |= MAC_CTRL_DUPLX;
else ctrl |= (((u32)adapter->hw.preamble_len &
ctrl &= ~MAC_CTRL_MC_ALL_EN; MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
if (adapter->vlgrp)
/* turn on broadcast mode if wake on-BC is enabled */ ctrl |= MAC_CTRL_RMV_VLAN;
if (wufc & ATLX_WUFC_BC) if (wufc & ATLX_WUFC_MAG)
ctrl |= MAC_CTRL_BC_EN; ctrl |= MAC_CTRL_BC_EN;
else
ctrl &= ~MAC_CTRL_BC_EN;
/* enable RX */
ctrl |= MAC_CTRL_RX_EN;
iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL); iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL);
pci_enable_wake(pdev, PCI_D3hot, 1); ioread32(hw->hw_addr + REG_MAC_CTRL);
pci_enable_wake(pdev, PCI_D3cold, 1);
} else { /* poke the PHY */
iowrite32(0, hw->hw_addr + REG_WOL_CTRL); ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
pci_enable_wake(pdev, PCI_D3hot, 0); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
pci_enable_wake(pdev, PCI_D3cold, 0); iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
goto exit;
} }
pci_save_state(pdev); if (!val && wufc) {
pci_disable_device(pdev); ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL);
ioread32(hw->hw_addr + REG_WOL_CTRL);
iowrite32(0, hw->hw_addr + REG_MAC_CTRL);
ioread32(hw->hw_addr + REG_MAC_CTRL);
hw->phy_configured = false;
pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
goto exit;
}
pci_set_power_state(pdev, PCI_D3hot); disable_wol:
iowrite32(0, hw->hw_addr + REG_WOL_CTRL);
ioread32(hw->hw_addr + REG_WOL_CTRL);
ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
atl1_phy_enter_power_saving(hw);
hw->phy_configured = false;
pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
exit:
if (netif_running(netdev))
pci_disable_msi(adapter->pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0; return 0;
} }
@ -2855,20 +2880,26 @@ static int atl1_resume(struct pci_dev *pdev)
pci_set_power_state(pdev, PCI_D0); pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev); pci_restore_state(pdev);
/* FIXME: check and handle */
err = pci_enable_device(pdev); err = pci_enable_device(pdev);
if (err) {
if (netif_msg_ifup(adapter))
dev_printk(KERN_DEBUG, &pdev->dev,
"error enabling pci device\n");
return err;
}
pci_set_master(pdev);
iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL);
pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3hot, 0);
pci_enable_wake(pdev, PCI_D3cold, 0); pci_enable_wake(pdev, PCI_D3cold, 0);
iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL); atl1_reset_hw(&adapter->hw);
atl1_reset(adapter); adapter->cmb.cmb->int_stats = 0;
if (netif_running(netdev)) if (netif_running(netdev))
atl1_up(adapter); atl1_up(adapter);
netif_device_attach(netdev); netif_device_attach(netdev);
atl1_via_workaround(adapter);
return 0; return 0;
} }
#else #else
@ -2876,6 +2907,13 @@ static int atl1_resume(struct pci_dev *pdev)
#define atl1_resume NULL #define atl1_resume NULL
#endif #endif
static void atl1_shutdown(struct pci_dev *pdev)
{
#ifdef CONFIG_PM
atl1_suspend(pdev, PMSG_SUSPEND);
#endif
}
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
static void atl1_poll_controller(struct net_device *netdev) static void atl1_poll_controller(struct net_device *netdev)
{ {
@ -3122,7 +3160,8 @@ static struct pci_driver atl1_driver = {
.probe = atl1_probe, .probe = atl1_probe,
.remove = __devexit_p(atl1_remove), .remove = __devexit_p(atl1_remove),
.suspend = atl1_suspend, .suspend = atl1_suspend,
.resume = atl1_resume .resume = atl1_resume,
.shutdown = atl1_shutdown
}; };
/* /*

View File

@ -1,7 +1,7 @@
/* /*
* Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
* Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com> * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
* Copyright(c) 2006 Jay Cliburn <jcliburn@gmail.com> * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
* *
* Derived from Intel e1000 driver * Derived from Intel e1000 driver
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.

View File

@ -2,7 +2,7 @@
* *
* Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
* Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com> * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
* Copyright(c) 2006 Jay Cliburn <jcliburn@gmail.com> * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
* Copyright(c) 2007 Atheros Corporation. All rights reserved. * Copyright(c) 2007 Atheros Corporation. All rights reserved.
* *
* Derived from Intel e1000 driver * Derived from Intel e1000 driver

View File

@ -2,7 +2,7 @@
* *
* Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
* Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com> * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
* Copyright(c) 2006 Jay Cliburn <jcliburn@gmail.com> * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
* Copyright(c) 2007 Atheros Corporation. All rights reserved. * Copyright(c) 2007 Atheros Corporation. All rights reserved.
* *
* Derived from Intel e1000 driver * Derived from Intel e1000 driver
@ -29,7 +29,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#define ATLX_DRIVER_VERSION "2.1.1" #define ATLX_DRIVER_VERSION "2.1.3"
MODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, \ MODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, \
Chris Snook <csnook@redhat.com>, Jay Cliburn <jcliburn@gmail.com>"); Chris Snook <csnook@redhat.com>, Jay Cliburn <jcliburn@gmail.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
@ -460,6 +460,9 @@ MODULE_VERSION(ATLX_DRIVER_VERSION);
#define MII_ATLX_PSSR_100MBS 0x4000 /* 01=100Mbs */ #define MII_ATLX_PSSR_100MBS 0x4000 /* 01=100Mbs */
#define MII_ATLX_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ #define MII_ATLX_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
#define MII_DBG_ADDR 0x1D
#define MII_DBG_DATA 0x1E
/* PCI Command Register Bit Definitions */ /* PCI Command Register Bit Definitions */
#define PCI_REG_COMMAND 0x04 /* PCI Command Register */ #define PCI_REG_COMMAND 0x04 /* PCI Command Register */
#define CMD_IO_SPACE 0x0001 #define CMD_IO_SPACE 0x0001

View File

@ -71,6 +71,7 @@ enum { /* adapter flags */
USING_MSIX = (1 << 2), USING_MSIX = (1 << 2),
QUEUES_BOUND = (1 << 3), QUEUES_BOUND = (1 << 3),
TP_PARITY_INIT = (1 << 4), TP_PARITY_INIT = (1 << 4),
NAPI_INIT = (1 << 5),
}; };
struct fl_pg_chunk { struct fl_pg_chunk {

View File

@ -698,6 +698,7 @@ void mac_prep(struct cmac *mac, struct adapter *adapter, int index);
void early_hw_init(struct adapter *adapter, const struct adapter_info *ai); void early_hw_init(struct adapter *adapter, const struct adapter_info *ai);
int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai,
int reset); int reset);
int t3_replay_prep_adapter(struct adapter *adapter);
void t3_led_ready(struct adapter *adapter); void t3_led_ready(struct adapter *adapter);
void t3_fatal_err(struct adapter *adapter); void t3_fatal_err(struct adapter *adapter);
void t3_set_vlan_accel(struct adapter *adapter, unsigned int ports, int on); void t3_set_vlan_accel(struct adapter *adapter, unsigned int ports, int on);

View File

@ -421,6 +421,13 @@ static void init_napi(struct adapter *adap)
netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll, netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll,
64); 64);
} }
/*
* netif_napi_add() can be called only once per napi_struct because it
* adds each new napi_struct to a list. Be careful not to call it a
* second time, e.g., during EEH recovery, by making a note of it.
*/
adap->flags |= NAPI_INIT;
} }
/* /*
@ -896,7 +903,8 @@ static int cxgb_up(struct adapter *adap)
goto out; goto out;
setup_rss(adap); setup_rss(adap);
init_napi(adap); if (!(adap->flags & NAPI_INIT))
init_napi(adap);
adap->flags |= FULL_INIT_DONE; adap->flags |= FULL_INIT_DONE;
} }
@ -999,7 +1007,7 @@ static int offload_open(struct net_device *dev)
return 0; return 0;
if (!adap_up && (err = cxgb_up(adapter)) < 0) if (!adap_up && (err = cxgb_up(adapter)) < 0)
return err; goto out;
t3_tp_set_offload_mode(adapter, 1); t3_tp_set_offload_mode(adapter, 1);
tdev->lldev = adapter->port[0]; tdev->lldev = adapter->port[0];
@ -1061,10 +1069,8 @@ static int cxgb_open(struct net_device *dev)
int other_ports = adapter->open_device_map & PORT_MASK; int other_ports = adapter->open_device_map & PORT_MASK;
int err; int err;
if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) { if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0)
quiesce_rx(adapter);
return err; return err;
}
set_bit(pi->port_id, &adapter->open_device_map); set_bit(pi->port_id, &adapter->open_device_map);
if (is_offload(adapter) && !ofld_disable) { if (is_offload(adapter) && !ofld_disable) {
@ -2424,14 +2430,11 @@ static pci_ers_result_t t3_io_error_detected(struct pci_dev *pdev,
test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map))
offload_close(&adapter->tdev); offload_close(&adapter->tdev);
/* Free sge resources */
t3_free_sge_resources(adapter);
adapter->flags &= ~FULL_INIT_DONE; adapter->flags &= ~FULL_INIT_DONE;
pci_disable_device(pdev); pci_disable_device(pdev);
/* Request a slot slot reset. */ /* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET; return PCI_ERS_RESULT_NEED_RESET;
} }
@ -2448,13 +2451,20 @@ static pci_ers_result_t t3_io_slot_reset(struct pci_dev *pdev)
if (pci_enable_device(pdev)) { if (pci_enable_device(pdev)) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Cannot re-enable PCI device after reset.\n"); "Cannot re-enable PCI device after reset.\n");
return PCI_ERS_RESULT_DISCONNECT; goto err;
} }
pci_set_master(pdev); pci_set_master(pdev);
pci_restore_state(pdev);
t3_prep_adapter(adapter, adapter->params.info, 1); /* Free sge resources */
t3_free_sge_resources(adapter);
if (t3_replay_prep_adapter(adapter))
goto err;
return PCI_ERS_RESULT_RECOVERED; return PCI_ERS_RESULT_RECOVERED;
err:
return PCI_ERS_RESULT_DISCONNECT;
} }
/** /**
@ -2483,13 +2493,6 @@ static void t3_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev); netif_device_attach(netdev);
} }
} }
if (is_offload(adapter)) {
__set_bit(OFFLOAD_DEVMAP_BIT, &adapter->registered_device_map);
if (offload_open(adapter->port[0]))
printk(KERN_WARNING
"Could not bring back offload capabilities\n");
}
} }
static struct pci_error_handlers t3_err_handler = { static struct pci_error_handlers t3_err_handler = {
@ -2608,6 +2611,7 @@ static int __devinit init_one(struct pci_dev *pdev,
} }
pci_set_master(pdev); pci_set_master(pdev);
pci_save_state(pdev);
mmio_start = pci_resource_start(pdev, 0); mmio_start = pci_resource_start(pdev, 0);
mmio_len = pci_resource_len(pdev, 0); mmio_len = pci_resource_len(pdev, 0);

View File

@ -444,6 +444,14 @@
#define A_PCIE_CFG 0x88 #define A_PCIE_CFG 0x88
#define S_ENABLELINKDWNDRST 21
#define V_ENABLELINKDWNDRST(x) ((x) << S_ENABLELINKDWNDRST)
#define F_ENABLELINKDWNDRST V_ENABLELINKDWNDRST(1U)
#define S_ENABLELINKDOWNRST 20
#define V_ENABLELINKDOWNRST(x) ((x) << S_ENABLELINKDOWNRST)
#define F_ENABLELINKDOWNRST V_ENABLELINKDOWNRST(1U)
#define S_PCIE_CLIDECEN 16 #define S_PCIE_CLIDECEN 16
#define V_PCIE_CLIDECEN(x) ((x) << S_PCIE_CLIDECEN) #define V_PCIE_CLIDECEN(x) ((x) << S_PCIE_CLIDECEN)
#define F_PCIE_CLIDECEN V_PCIE_CLIDECEN(1U) #define F_PCIE_CLIDECEN V_PCIE_CLIDECEN(1U)

View File

@ -538,6 +538,31 @@ static void *alloc_ring(struct pci_dev *pdev, size_t nelem, size_t elem_size,
return p; return p;
} }
/**
* t3_reset_qset - reset a sge qset
* @q: the queue set
*
* Reset the qset structure.
* the NAPI structure is preserved in the event of
* the qset's reincarnation, for example during EEH recovery.
*/
static void t3_reset_qset(struct sge_qset *q)
{
if (q->adap &&
!(q->adap->flags & NAPI_INIT)) {
memset(q, 0, sizeof(*q));
return;
}
q->adap = NULL;
memset(&q->rspq, 0, sizeof(q->rspq));
memset(q->fl, 0, sizeof(struct sge_fl) * SGE_RXQ_PER_SET);
memset(q->txq, 0, sizeof(struct sge_txq) * SGE_TXQ_PER_SET);
q->txq_stopped = 0;
memset(&q->tx_reclaim_timer, 0, sizeof(q->tx_reclaim_timer));
}
/** /**
* free_qset - free the resources of an SGE queue set * free_qset - free the resources of an SGE queue set
* @adapter: the adapter owning the queue set * @adapter: the adapter owning the queue set
@ -594,7 +619,7 @@ static void t3_free_qset(struct adapter *adapter, struct sge_qset *q)
q->rspq.desc, q->rspq.phys_addr); q->rspq.desc, q->rspq.phys_addr);
} }
memset(q, 0, sizeof(*q)); t3_reset_qset(q);
} }
/** /**
@ -1365,7 +1390,7 @@ static void restart_ctrlq(unsigned long data)
*/ */
int t3_mgmt_tx(struct adapter *adap, struct sk_buff *skb) int t3_mgmt_tx(struct adapter *adap, struct sk_buff *skb)
{ {
int ret; int ret;
local_bh_disable(); local_bh_disable();
ret = ctrl_xmit(adap, &adap->sge.qs[0].txq[TXQ_CTRL], skb); ret = ctrl_xmit(adap, &adap->sge.qs[0].txq[TXQ_CTRL], skb);
local_bh_enable(); local_bh_enable();

View File

@ -3264,6 +3264,7 @@ static void config_pcie(struct adapter *adap)
t3_write_reg(adap, A_PCIE_PEX_ERR, 0xffffffff); t3_write_reg(adap, A_PCIE_PEX_ERR, 0xffffffff);
t3_set_reg_field(adap, A_PCIE_CFG, 0, t3_set_reg_field(adap, A_PCIE_CFG, 0,
F_ENABLELINKDWNDRST | F_ENABLELINKDOWNRST |
F_PCIE_DMASTOPEN | F_PCIE_CLIDECEN); F_PCIE_DMASTOPEN | F_PCIE_CLIDECEN);
} }
@ -3655,3 +3656,30 @@ void t3_led_ready(struct adapter *adapter)
t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL,
F_GPIO0_OUT_VAL); F_GPIO0_OUT_VAL);
} }
int t3_replay_prep_adapter(struct adapter *adapter)
{
const struct adapter_info *ai = adapter->params.info;
unsigned int i, j = 0;
int ret;
early_hw_init(adapter, ai);
ret = init_parity(adapter);
if (ret)
return ret;
for_each_port(adapter, i) {
struct port_info *p = adap2pinfo(adapter, i);
while (!adapter->params.vpd.port_type[j])
++j;
p->port_type->phy_prep(&p->phy, adapter, ai->phy_base_addr + j,
ai->mdio_ops);
p->phy.ops->power_down(&p->phy, 1);
++j;
}
return 0;
}

View File

@ -117,6 +117,9 @@ typedef struct board_info {
struct mutex addr_lock; /* phy and eeprom access lock */ struct mutex addr_lock; /* phy and eeprom access lock */
struct delayed_work phy_poll;
struct net_device *ndev;
spinlock_t lock; spinlock_t lock;
struct mii_if_info mii; struct mii_if_info mii;
@ -297,6 +300,10 @@ static void dm9000_set_io(struct board_info *db, int byte_width)
} }
} }
static void dm9000_schedule_poll(board_info_t *db)
{
schedule_delayed_work(&db->phy_poll, HZ * 2);
}
/* Our watchdog timed out. Called by the networking layer */ /* Our watchdog timed out. Called by the networking layer */
static void dm9000_timeout(struct net_device *dev) static void dm9000_timeout(struct net_device *dev)
@ -465,6 +472,17 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
.set_eeprom = dm9000_set_eeprom, .set_eeprom = dm9000_set_eeprom,
}; };
static void
dm9000_poll_work(struct work_struct *w)
{
struct delayed_work *dw = container_of(w, struct delayed_work, work);
board_info_t *db = container_of(dw, board_info_t, phy_poll);
mii_check_media(&db->mii, netif_msg_link(db), 0);
if (netif_running(db->ndev))
dm9000_schedule_poll(db);
}
/* dm9000_release_board /* dm9000_release_board
* *
@ -503,7 +521,7 @@ dm9000_release_board(struct platform_device *pdev, struct board_info *db)
/* /*
* Search DM9000 board, allocate space and register it * Search DM9000 board, allocate space and register it
*/ */
static int static int __devinit
dm9000_probe(struct platform_device *pdev) dm9000_probe(struct platform_device *pdev)
{ {
struct dm9000_plat_data *pdata = pdev->dev.platform_data; struct dm9000_plat_data *pdata = pdev->dev.platform_data;
@ -525,17 +543,21 @@ dm9000_probe(struct platform_device *pdev)
SET_NETDEV_DEV(ndev, &pdev->dev); SET_NETDEV_DEV(ndev, &pdev->dev);
dev_dbg(&pdev->dev, "dm9000_probe()"); dev_dbg(&pdev->dev, "dm9000_probe()\n");
/* setup board info structure */ /* setup board info structure */
db = (struct board_info *) ndev->priv; db = (struct board_info *) ndev->priv;
memset(db, 0, sizeof (*db)); memset(db, 0, sizeof (*db));
db->dev = &pdev->dev; db->dev = &pdev->dev;
db->ndev = ndev;
spin_lock_init(&db->lock); spin_lock_init(&db->lock);
mutex_init(&db->addr_lock); mutex_init(&db->addr_lock);
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
if (pdev->num_resources < 2) { if (pdev->num_resources < 2) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
@ -761,6 +783,8 @@ dm9000_open(struct net_device *dev)
mii_check_media(&db->mii, netif_msg_link(db), 1); mii_check_media(&db->mii, netif_msg_link(db), 1);
netif_start_queue(dev); netif_start_queue(dev);
dm9000_schedule_poll(db);
return 0; return 0;
} }
@ -879,6 +903,8 @@ dm9000_stop(struct net_device *ndev)
if (netif_msg_ifdown(db)) if (netif_msg_ifdown(db))
dev_dbg(db->dev, "shutting down %s\n", ndev->name); dev_dbg(db->dev, "shutting down %s\n", ndev->name);
cancel_delayed_work(&db->phy_poll);
netif_stop_queue(ndev); netif_stop_queue(ndev);
netif_carrier_off(ndev); netif_carrier_off(ndev);
@ -1288,6 +1314,8 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
spin_unlock_irqrestore(&db->lock,flags); spin_unlock_irqrestore(&db->lock,flags);
mutex_unlock(&db->addr_lock); mutex_unlock(&db->addr_lock);
dm9000_dbg(db, 5, "phy_read[%02x] -> %04x\n", reg, ret);
return ret; return ret;
} }
@ -1301,6 +1329,7 @@ dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value)
unsigned long flags; unsigned long flags;
unsigned long reg_save; unsigned long reg_save;
dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value);
mutex_lock(&db->addr_lock); mutex_lock(&db->addr_lock);
spin_lock_irqsave(&db->lock,flags); spin_lock_irqsave(&db->lock,flags);
@ -1372,7 +1401,7 @@ dm9000_drv_resume(struct platform_device *dev)
return 0; return 0;
} }
static int static int __devexit
dm9000_drv_remove(struct platform_device *pdev) dm9000_drv_remove(struct platform_device *pdev)
{ {
struct net_device *ndev = platform_get_drvdata(pdev); struct net_device *ndev = platform_get_drvdata(pdev);
@ -1393,7 +1422,7 @@ static struct platform_driver dm9000_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = dm9000_probe, .probe = dm9000_probe,
.remove = dm9000_drv_remove, .remove = __devexit_p(dm9000_drv_remove),
.suspend = dm9000_drv_suspend, .suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume, .resume = dm9000_drv_resume,
}; };

View File

@ -40,7 +40,7 @@
#include <asm/io.h> #include <asm/io.h>
#define DRV_NAME "ehea" #define DRV_NAME "ehea"
#define DRV_VERSION "EHEA_0090" #define DRV_VERSION "EHEA_0091"
/* eHEA capability flags */ /* eHEA capability flags */
#define DLPAR_PORT_ADD_REM 1 #define DLPAR_PORT_ADD_REM 1
@ -118,6 +118,13 @@
#define EHEA_MR_ACC_CTRL 0x00800000 #define EHEA_MR_ACC_CTRL 0x00800000
#define EHEA_BUSMAP_START 0x8000000000000000ULL #define EHEA_BUSMAP_START 0x8000000000000000ULL
#define EHEA_INVAL_ADDR 0xFFFFFFFFFFFFFFFFULL
#define EHEA_DIR_INDEX_SHIFT 13 /* 8k Entries in 64k block */
#define EHEA_TOP_INDEX_SHIFT (EHEA_DIR_INDEX_SHIFT * 2)
#define EHEA_MAP_ENTRIES (1 << EHEA_DIR_INDEX_SHIFT)
#define EHEA_MAP_SIZE (0x10000) /* currently fixed map size */
#define EHEA_INDEX_MASK (EHEA_MAP_ENTRIES - 1)
#define EHEA_WATCH_DOG_TIMEOUT 10*HZ #define EHEA_WATCH_DOG_TIMEOUT 10*HZ
@ -192,10 +199,20 @@ struct h_epas {
set to 0 if unused */ set to 0 if unused */
}; };
struct ehea_busmap { /*
unsigned int entries; /* total number of entries */ * Memory map data structures
unsigned int valid_sections; /* number of valid sections */ */
u64 *vaddr; struct ehea_dir_bmap
{
u64 ent[EHEA_MAP_ENTRIES];
};
struct ehea_top_bmap
{
struct ehea_dir_bmap *dir[EHEA_MAP_ENTRIES];
};
struct ehea_bmap
{
struct ehea_top_bmap *top[EHEA_MAP_ENTRIES];
}; };
struct ehea_qp; struct ehea_qp;

View File

@ -35,6 +35,7 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/memory.h>
#include <asm/kexec.h> #include <asm/kexec.h>
#include <linux/mutex.h> #include <linux/mutex.h>
@ -3503,6 +3504,24 @@ void ehea_crash_handler(void)
0, H_DEREG_BCMC); 0, H_DEREG_BCMC);
} }
static int ehea_mem_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
switch (action) {
case MEM_OFFLINE:
ehea_info("memory has been removed");
ehea_rereg_mrs(NULL);
break;
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block ehea_mem_nb = {
.notifier_call = ehea_mem_notifier,
};
static int ehea_reboot_notifier(struct notifier_block *nb, static int ehea_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused) unsigned long action, void *unused)
{ {
@ -3581,6 +3600,10 @@ int __init ehea_module_init(void)
if (ret) if (ret)
ehea_info("failed registering reboot notifier"); ehea_info("failed registering reboot notifier");
ret = register_memory_notifier(&ehea_mem_nb);
if (ret)
ehea_info("failed registering memory remove notifier");
ret = crash_shutdown_register(&ehea_crash_handler); ret = crash_shutdown_register(&ehea_crash_handler);
if (ret) if (ret)
ehea_info("failed registering crash handler"); ehea_info("failed registering crash handler");
@ -3604,6 +3627,7 @@ int __init ehea_module_init(void)
out3: out3:
ibmebus_unregister_driver(&ehea_driver); ibmebus_unregister_driver(&ehea_driver);
out2: out2:
unregister_memory_notifier(&ehea_mem_nb);
unregister_reboot_notifier(&ehea_reboot_nb); unregister_reboot_notifier(&ehea_reboot_nb);
crash_shutdown_unregister(&ehea_crash_handler); crash_shutdown_unregister(&ehea_crash_handler);
out: out:
@ -3621,6 +3645,7 @@ static void __exit ehea_module_exit(void)
ret = crash_shutdown_unregister(&ehea_crash_handler); ret = crash_shutdown_unregister(&ehea_crash_handler);
if (ret) if (ret)
ehea_info("failed unregistering crash handler"); ehea_info("failed unregistering crash handler");
unregister_memory_notifier(&ehea_mem_nb);
kfree(ehea_fw_handles.arr); kfree(ehea_fw_handles.arr);
kfree(ehea_bcmc_regs.arr); kfree(ehea_bcmc_regs.arr);
ehea_destroy_busmap(); ehea_destroy_busmap();

View File

@ -31,8 +31,8 @@
#include "ehea_phyp.h" #include "ehea_phyp.h"
#include "ehea_qmr.h" #include "ehea_qmr.h"
struct ehea_bmap *ehea_bmap = NULL;
struct ehea_busmap ehea_bmap = { 0, 0, NULL };
static void *hw_qpageit_get_inc(struct hw_queue *queue) static void *hw_qpageit_get_inc(struct hw_queue *queue)
@ -559,125 +559,253 @@ int ehea_destroy_qp(struct ehea_qp *qp)
return 0; return 0;
} }
int ehea_create_busmap(void) static inline int ehea_calc_index(unsigned long i, unsigned long s)
{ {
u64 vaddr = EHEA_BUSMAP_START; return (i >> s) & EHEA_INDEX_MASK;
unsigned long high_section_index = 0; }
int i;
/* static inline int ehea_init_top_bmap(struct ehea_top_bmap *ehea_top_bmap,
* Sections are not in ascending order -> Loop over all sections and int dir)
* find the highest PFN to compute the required map size. {
*/ if(!ehea_top_bmap->dir[dir]) {
ehea_bmap.valid_sections = 0; ehea_top_bmap->dir[dir] =
kzalloc(sizeof(struct ehea_dir_bmap), GFP_KERNEL);
if (!ehea_top_bmap->dir[dir])
return -ENOMEM;
}
return 0;
}
for (i = 0; i < NR_MEM_SECTIONS; i++) static inline int ehea_init_bmap(struct ehea_bmap *ehea_bmap, int top, int dir)
if (valid_section_nr(i)) {
high_section_index = i; if(!ehea_bmap->top[top]) {
ehea_bmap->top[top] =
kzalloc(sizeof(struct ehea_top_bmap), GFP_KERNEL);
if (!ehea_bmap->top[top])
return -ENOMEM;
}
return ehea_init_top_bmap(ehea_bmap->top[top], dir);
}
ehea_bmap.entries = high_section_index + 1; static int ehea_create_busmap_callback(unsigned long pfn,
ehea_bmap.vaddr = vmalloc(ehea_bmap.entries * sizeof(*ehea_bmap.vaddr)); unsigned long nr_pages, void *arg)
{
unsigned long i, mr_len, start_section, end_section;
start_section = (pfn * PAGE_SIZE) / EHEA_SECTSIZE;
end_section = start_section + ((nr_pages * PAGE_SIZE) / EHEA_SECTSIZE);
mr_len = *(unsigned long *)arg;
if (!ehea_bmap.vaddr) ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL);
if (!ehea_bmap)
return -ENOMEM; return -ENOMEM;
for (i = 0 ; i < ehea_bmap.entries; i++) { for (i = start_section; i < end_section; i++) {
unsigned long pfn = section_nr_to_pfn(i); int ret;
int top, dir, idx;
u64 vaddr;
if (pfn_valid(pfn)) { top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
ehea_bmap.vaddr[i] = vaddr; dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);
vaddr += EHEA_SECTSIZE;
ehea_bmap.valid_sections++; ret = ehea_init_bmap(ehea_bmap, top, dir);
} else if(ret)
ehea_bmap.vaddr[i] = 0; return ret;
idx = i & EHEA_INDEX_MASK;
vaddr = EHEA_BUSMAP_START + mr_len + i * EHEA_SECTSIZE;
ehea_bmap->top[top]->dir[dir]->ent[idx] = vaddr;
} }
mr_len += nr_pages * PAGE_SIZE;
*(unsigned long *)arg = mr_len;
return 0; return 0;
} }
static unsigned long ehea_mr_len;
static DEFINE_MUTEX(ehea_busmap_mutex);
int ehea_create_busmap(void)
{
int ret;
mutex_lock(&ehea_busmap_mutex);
ehea_mr_len = 0;
ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, &ehea_mr_len,
ehea_create_busmap_callback);
mutex_unlock(&ehea_busmap_mutex);
return ret;
}
void ehea_destroy_busmap(void) void ehea_destroy_busmap(void)
{ {
vfree(ehea_bmap.vaddr); int top, dir;
mutex_lock(&ehea_busmap_mutex);
if (!ehea_bmap)
goto out_destroy;
for (top = 0; top < EHEA_MAP_ENTRIES; top++) {
if (!ehea_bmap->top[top])
continue;
for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) {
if (!ehea_bmap->top[top]->dir[dir])
continue;
kfree(ehea_bmap->top[top]->dir[dir]);
}
kfree(ehea_bmap->top[top]);
}
kfree(ehea_bmap);
ehea_bmap = NULL;
out_destroy:
mutex_unlock(&ehea_busmap_mutex);
} }
u64 ehea_map_vaddr(void *caddr) u64 ehea_map_vaddr(void *caddr)
{ {
u64 mapped_addr; int top, dir, idx;
unsigned long index = __pa(caddr) >> SECTION_SIZE_BITS; unsigned long index, offset;
if (likely(index < ehea_bmap.entries)) { if (!ehea_bmap)
mapped_addr = ehea_bmap.vaddr[index]; return EHEA_INVAL_ADDR;
if (likely(mapped_addr))
mapped_addr |= (((unsigned long)caddr)
& (EHEA_SECTSIZE - 1));
else
mapped_addr = -1;
} else
mapped_addr = -1;
if (unlikely(mapped_addr == -1)) index = virt_to_abs(caddr) >> SECTION_SIZE_BITS;
if (!test_and_set_bit(__EHEA_STOP_XFER, &ehea_driver_flags)) top = (index >> EHEA_TOP_INDEX_SHIFT) & EHEA_INDEX_MASK;
schedule_work(&ehea_rereg_mr_task); if (!ehea_bmap->top[top])
return EHEA_INVAL_ADDR;
return mapped_addr; dir = (index >> EHEA_DIR_INDEX_SHIFT) & EHEA_INDEX_MASK;
if (!ehea_bmap->top[top]->dir[dir])
return EHEA_INVAL_ADDR;
idx = index & EHEA_INDEX_MASK;
if (!ehea_bmap->top[top]->dir[dir]->ent[idx])
return EHEA_INVAL_ADDR;
offset = (unsigned long)caddr & (EHEA_SECTSIZE - 1);
return ehea_bmap->top[top]->dir[dir]->ent[idx] | offset;
}
static inline void *ehea_calc_sectbase(int top, int dir, int idx)
{
unsigned long ret = idx;
ret |= dir << EHEA_DIR_INDEX_SHIFT;
ret |= top << EHEA_TOP_INDEX_SHIFT;
return abs_to_virt(ret << SECTION_SIZE_BITS);
}
static u64 ehea_reg_mr_section(int top, int dir, int idx, u64 *pt,
struct ehea_adapter *adapter,
struct ehea_mr *mr)
{
void *pg;
u64 j, m, hret;
unsigned long k = 0;
u64 pt_abs = virt_to_abs(pt);
void *sectbase = ehea_calc_sectbase(top, dir, idx);
for (j = 0; j < (EHEA_PAGES_PER_SECTION / EHEA_MAX_RPAGE); j++) {
for (m = 0; m < EHEA_MAX_RPAGE; m++) {
pg = sectbase + ((k++) * EHEA_PAGESIZE);
pt[m] = virt_to_abs(pg);
}
hret = ehea_h_register_rpage_mr(adapter->handle, mr->handle, 0,
0, pt_abs, EHEA_MAX_RPAGE);
if ((hret != H_SUCCESS)
&& (hret != H_PAGE_REGISTERED)) {
ehea_h_free_resource(adapter->handle, mr->handle,
FORCE_FREE);
ehea_error("register_rpage_mr failed");
return hret;
}
}
return hret;
}
static u64 ehea_reg_mr_sections(int top, int dir, u64 *pt,
struct ehea_adapter *adapter,
struct ehea_mr *mr)
{
u64 hret = H_SUCCESS;
int idx;
for (idx = 0; idx < EHEA_MAP_ENTRIES; idx++) {
if (!ehea_bmap->top[top]->dir[dir]->ent[idx])
continue;
hret = ehea_reg_mr_section(top, dir, idx, pt, adapter, mr);
if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))
return hret;
}
return hret;
}
static u64 ehea_reg_mr_dir_sections(int top, u64 *pt,
struct ehea_adapter *adapter,
struct ehea_mr *mr)
{
u64 hret = H_SUCCESS;
int dir;
for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) {
if (!ehea_bmap->top[top]->dir[dir])
continue;
hret = ehea_reg_mr_sections(top, dir, pt, adapter, mr);
if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))
return hret;
}
return hret;
} }
int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr) int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
{ {
int ret; int ret;
u64 *pt; u64 *pt;
void *pg; u64 hret;
u64 hret, pt_abs, i, j, m, mr_len;
u32 acc_ctrl = EHEA_MR_ACC_CTRL; u32 acc_ctrl = EHEA_MR_ACC_CTRL;
mr_len = ehea_bmap.valid_sections * EHEA_SECTSIZE; unsigned long top;
pt = kzalloc(PAGE_SIZE, GFP_KERNEL); pt = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!pt) { if (!pt) {
ehea_error("no mem"); ehea_error("no mem");
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
pt_abs = virt_to_abs(pt);
hret = ehea_h_alloc_resource_mr(adapter->handle, hret = ehea_h_alloc_resource_mr(adapter->handle, EHEA_BUSMAP_START,
EHEA_BUSMAP_START, mr_len, ehea_mr_len, acc_ctrl, adapter->pd,
acc_ctrl, adapter->pd,
&mr->handle, &mr->lkey); &mr->handle, &mr->lkey);
if (hret != H_SUCCESS) { if (hret != H_SUCCESS) {
ehea_error("alloc_resource_mr failed"); ehea_error("alloc_resource_mr failed");
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
for (i = 0 ; i < ehea_bmap.entries; i++) if (!ehea_bmap) {
if (ehea_bmap.vaddr[i]) { ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE);
void *sectbase = __va(i << SECTION_SIZE_BITS); ehea_error("no busmap available");
unsigned long k = 0; ret = -EIO;
goto out;
}
for (j = 0; j < (EHEA_PAGES_PER_SECTION / for (top = 0; top < EHEA_MAP_ENTRIES; top++) {
EHEA_MAX_RPAGE); j++) { if (!ehea_bmap->top[top])
continue;
for (m = 0; m < EHEA_MAX_RPAGE; m++) { hret = ehea_reg_mr_dir_sections(top, pt, adapter, mr);
pg = sectbase + ((k++) * EHEA_PAGESIZE); if((hret != H_PAGE_REGISTERED) && (hret != H_SUCCESS))
pt[m] = virt_to_abs(pg); break;
} }
hret = ehea_h_register_rpage_mr(adapter->handle,
mr->handle,
0, 0, pt_abs,
EHEA_MAX_RPAGE);
if ((hret != H_SUCCESS)
&& (hret != H_PAGE_REGISTERED)) {
ehea_h_free_resource(adapter->handle,
mr->handle,
FORCE_FREE);
ehea_error("register_rpage_mr failed");
ret = -EIO;
goto out;
}
}
}
if (hret != H_SUCCESS) { if (hret != H_SUCCESS) {
ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE); ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE);

View File

@ -635,6 +635,8 @@ static void free_skb_resources(struct gfar_private *priv)
dev_kfree_skb_any(priv->tx_skbuff[i]); dev_kfree_skb_any(priv->tx_skbuff[i]);
priv->tx_skbuff[i] = NULL; priv->tx_skbuff[i] = NULL;
} }
txbdp++;
} }
kfree(priv->tx_skbuff); kfree(priv->tx_skbuff);

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ struct mcp_dma_addr {
__be32 low; __be32 low;
}; };
/* 4 Bytes. 8 Bytes for NDIS drivers. */ /* 4 Bytes */
struct mcp_slot { struct mcp_slot {
__sum16 checksum; __sum16 checksum;
__be16 length; __be16 length;
@ -144,6 +144,7 @@ enum myri10ge_mcp_cmd_type {
* a power of 2 number of entries. */ * a power of 2 number of entries. */
MXGEFW_CMD_SET_INTRQ_SIZE, /* in bytes */ MXGEFW_CMD_SET_INTRQ_SIZE, /* in bytes */
#define MXGEFW_CMD_SET_INTRQ_SIZE_FLAG_NO_STRICT_SIZE_CHECK (1 << 31)
/* command to bring ethernet interface up. Above parameters /* command to bring ethernet interface up. Above parameters
* (plus mtu & mac address) must have been exchanged prior * (plus mtu & mac address) must have been exchanged prior
@ -221,10 +222,14 @@ enum myri10ge_mcp_cmd_type {
MXGEFW_CMD_GET_MAX_RSS_QUEUES, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
MXGEFW_CMD_ENABLE_RSS_QUEUES, MXGEFW_CMD_ENABLE_RSS_QUEUES,
/* data0 = number of slices n (0, 1, ..., n-1) to enable /* data0 = number of slices n (0, 1, ..., n-1) to enable
* data1 = interrupt mode. 0=share one INTx/MSI, 1=use one MSI-X per queue. * data1 = interrupt mode.
* 0=share one INTx/MSI, 1=use one MSI-X per queue.
* If all queues share one interrupt, the driver must have set * If all queues share one interrupt, the driver must have set
* RSS_SHARED_INTERRUPT_DMA before enabling queues. * RSS_SHARED_INTERRUPT_DMA before enabling queues.
*/ */
#define MXGEFW_SLICE_INTR_MODE_SHARED 0
#define MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE 1
MXGEFW_CMD_GET_RSS_SHARED_INTERRUPT_MASK_OFFSET, MXGEFW_CMD_GET_RSS_SHARED_INTERRUPT_MASK_OFFSET,
MXGEFW_CMD_SET_RSS_SHARED_INTERRUPT_DMA, MXGEFW_CMD_SET_RSS_SHARED_INTERRUPT_DMA,
/* data0, data1 = bus address lsw, msw */ /* data0, data1 = bus address lsw, msw */
@ -241,10 +246,14 @@ enum myri10ge_mcp_cmd_type {
* 0: disable rss. nic does not distribute receive packets. * 0: disable rss. nic does not distribute receive packets.
* 1: enable rss. nic distributes receive packets among queues. * 1: enable rss. nic distributes receive packets among queues.
* data1 = hash type * data1 = hash type
* 1: IPV4 * 1: IPV4 (required by RSS)
* 2: TCP_IPV4 * 2: TCP_IPV4 (required by RSS)
* 3: IPV4 | TCP_IPV4 * 3: IPV4 | TCP_IPV4 (required by RSS)
* 4: source port
*/ */
#define MXGEFW_RSS_HASH_TYPE_IPV4 0x1
#define MXGEFW_RSS_HASH_TYPE_TCP_IPV4 0x2
#define MXGEFW_RSS_HASH_TYPE_SRC_PORT 0x4
MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
/* Return data = the max. size of the entire headers of a IPv6 TSO packet. /* Return data = the max. size of the entire headers of a IPv6 TSO packet.
@ -260,6 +269,8 @@ enum myri10ge_mcp_cmd_type {
* 0: Linux/FreeBSD style (NIC default) * 0: Linux/FreeBSD style (NIC default)
* 1: NDIS/NetBSD style * 1: NDIS/NetBSD style
*/ */
#define MXGEFW_TSO_MODE_LINUX 0
#define MXGEFW_TSO_MODE_NDIS 1
MXGEFW_CMD_MDIO_READ, MXGEFW_CMD_MDIO_READ,
/* data0 = dev_addr (PMA/PMD or PCS ...), data1 = register/addr */ /* data0 = dev_addr (PMA/PMD or PCS ...), data1 = register/addr */
@ -286,6 +297,38 @@ enum myri10ge_mcp_cmd_type {
/* Return data = NIC memory offset of mcp_vpump_public_global */ /* Return data = NIC memory offset of mcp_vpump_public_global */
MXGEFW_CMD_RESET_VPUMP, MXGEFW_CMD_RESET_VPUMP,
/* Resets the VPUMP state */ /* Resets the VPUMP state */
MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE,
/* data0 = mcp_slot type to use.
* 0 = the default 4B mcp_slot
* 1 = 8B mcp_slot_8
*/
#define MXGEFW_RSS_MCP_SLOT_TYPE_MIN 0
#define MXGEFW_RSS_MCP_SLOT_TYPE_WITH_HASH 1
MXGEFW_CMD_SET_THROTTLE_FACTOR,
/* set the throttle factor for ethp_z8e
* data0 = throttle_factor
* throttle_factor = 256 * pcie-raw-speed / tx_speed
* tx_speed = 256 * pcie-raw-speed / throttle_factor
*
* For PCI-E x8: pcie-raw-speed == 16Gb/s
* For PCI-E x4: pcie-raw-speed == 8Gb/s
*
* ex1: throttle_factor == 0x1a0 (416), tx_speed == 1.23GB/s == 9.846 Gb/s
* ex2: throttle_factor == 0x200 (512), tx_speed == 1.0GB/s == 8 Gb/s
*
* with tx_boundary == 2048, max-throttle-factor == 8191 => min-speed == 500Mb/s
* with tx_boundary == 4096, max-throttle-factor == 4095 => min-speed == 1Gb/s
*/
MXGEFW_CMD_VPUMP_UP,
/* Allocates VPump Connection, Send Request and Zero copy buffer address tables */
MXGEFW_CMD_GET_VPUMP_CLK,
/* Get the lanai clock */
MXGEFW_CMD_GET_DCA_OFFSET,
/* offset of dca control for WDMAs */
}; };
enum myri10ge_mcp_cmd_status { enum myri10ge_mcp_cmd_status {
@ -302,7 +345,8 @@ enum myri10ge_mcp_cmd_status {
MXGEFW_CMD_ERROR_UNALIGNED, MXGEFW_CMD_ERROR_UNALIGNED,
MXGEFW_CMD_ERROR_NO_MDIO, MXGEFW_CMD_ERROR_NO_MDIO,
MXGEFW_CMD_ERROR_XFP_FAILURE, MXGEFW_CMD_ERROR_XFP_FAILURE,
MXGEFW_CMD_ERROR_XFP_ABSENT MXGEFW_CMD_ERROR_XFP_ABSENT,
MXGEFW_CMD_ERROR_BAD_PCIE_LINK
}; };
#define MXGEFW_OLD_IRQ_DATA_LEN 40 #define MXGEFW_OLD_IRQ_DATA_LEN 40

View File

@ -1,30 +1,6 @@
#ifndef __MYRI10GE_MCP_GEN_HEADER_H__ #ifndef __MYRI10GE_MCP_GEN_HEADER_H__
#define __MYRI10GE_MCP_GEN_HEADER_H__ #define __MYRI10GE_MCP_GEN_HEADER_H__
/* this file define a standard header used as a first entry point to
* exchange information between firmware/driver and driver. The
* header structure can be anywhere in the mcp. It will usually be in
* the .data section, because some fields needs to be initialized at
* compile time.
* The 32bit word at offset MX_HEADER_PTR_OFFSET in the mcp must
* contains the location of the header.
*
* Typically a MCP will start with the following:
* .text
* .space 52 ! to help catch MEMORY_INT errors
* bt start ! jump to real code
* nop
* .long _gen_mcp_header
*
* The source will have a definition like:
*
* mcp_gen_header_t gen_mcp_header = {
* .header_length = sizeof(mcp_gen_header_t),
* .mcp_type = MCP_TYPE_XXX,
* .version = "something $Id: mcp_gen_header.h,v 1.2 2006/05/13 10:04:35 bgoglin Exp $",
* .mcp_globals = (unsigned)&Globals
* };
*/
#define MCP_HEADER_PTR_OFFSET 0x3c #define MCP_HEADER_PTR_OFFSET 0x3c
@ -32,13 +8,14 @@
#define MCP_TYPE_PCIE 0x70636965 /* "PCIE" pcie-only MCP */ #define MCP_TYPE_PCIE 0x70636965 /* "PCIE" pcie-only MCP */
#define MCP_TYPE_ETH 0x45544820 /* "ETH " */ #define MCP_TYPE_ETH 0x45544820 /* "ETH " */
#define MCP_TYPE_MCP0 0x4d435030 /* "MCP0" */ #define MCP_TYPE_MCP0 0x4d435030 /* "MCP0" */
#define MCP_TYPE_DFLT 0x20202020 /* " " */
struct mcp_gen_header { struct mcp_gen_header {
/* the first 4 fields are filled at compile time */ /* the first 4 fields are filled at compile time */
unsigned header_length; unsigned header_length;
__be32 mcp_type; __be32 mcp_type;
char version[128]; char version[128];
unsigned mcp_globals; /* pointer to mcp-type specific structure */ unsigned mcp_private; /* pointer to mcp-type specific structure */
/* filled by the MCP at run-time */ /* filled by the MCP at run-time */
unsigned sram_size; unsigned sram_size;
@ -53,6 +30,18 @@ struct mcp_gen_header {
* *
* Never remove any field. Keep everything naturally align. * Never remove any field. Keep everything naturally align.
*/ */
/* Specifies if the running mcp is mcp0, 1, or 2. */
unsigned char mcp_index;
unsigned char disable_rabbit;
unsigned char unaligned_tlp;
unsigned char pad1;
unsigned counters_addr;
unsigned copy_block_info; /* for small mcps loaded with "lload -d" */
unsigned short handoff_id_major; /* must be equal */
unsigned short handoff_id_caps; /* bitfield: new mcp must have superset */
unsigned msix_table_addr; /* start address of msix table in firmware */
/* 8 */
}; };
#endif /* __MYRI10GE_MCP_GEN_HEADER_H__ */ #endif /* __MYRI10GE_MCP_GEN_HEADER_H__ */

View File

@ -865,7 +865,6 @@ static int link_status_1g_serdes(struct niu *np, int *link_up_p)
return 0; return 0;
} }
static int link_status_10g_serdes(struct niu *np, int *link_up_p) static int link_status_10g_serdes(struct niu *np, int *link_up_p)
{ {
unsigned long flags; unsigned long flags;
@ -900,7 +899,6 @@ static int link_status_10g_serdes(struct niu *np, int *link_up_p)
return 0; return 0;
} }
static int link_status_1g_rgmii(struct niu *np, int *link_up_p) static int link_status_1g_rgmii(struct niu *np, int *link_up_p)
{ {
struct niu_link_config *lp = &np->link_config; struct niu_link_config *lp = &np->link_config;
@ -957,7 +955,6 @@ out:
return err; return err;
} }
static int bcm8704_reset(struct niu *np) static int bcm8704_reset(struct niu *np)
{ {
int err, limit; int err, limit;
@ -1357,8 +1354,6 @@ static int mii_reset(struct niu *np)
return 0; return 0;
} }
static int xcvr_init_1g_rgmii(struct niu *np) static int xcvr_init_1g_rgmii(struct niu *np)
{ {
int err; int err;
@ -1419,7 +1414,6 @@ static int xcvr_init_1g_rgmii(struct niu *np)
return 0; return 0;
} }
static int mii_init_common(struct niu *np) static int mii_init_common(struct niu *np)
{ {
struct niu_link_config *lp = &np->link_config; struct niu_link_config *lp = &np->link_config;
@ -7008,31 +7002,20 @@ static int __devinit niu_phy_type_prop_decode(struct niu *np,
return 0; return 0;
} }
/* niu board models have a trailing dash version incremented
* with HW rev change. Need to ingnore the dash version while
* checking for match
*
* for example, for the 10G card the current vpd.board_model
* is 501-5283-04, of which -04 is the dash version and have
* to be ignored
*/
static int niu_board_model_match(struct niu *np, const char *model)
{
return !strncmp(np->vpd.board_model, model, strlen(model));
}
static int niu_pci_vpd_get_nports(struct niu *np) static int niu_pci_vpd_get_nports(struct niu *np)
{ {
int ports = 0; int ports = 0;
if ((niu_board_model_match(np, NIU_QGC_LP_BM_STR)) || if ((!strcmp(np->vpd.model, NIU_QGC_LP_MDL_STR)) ||
(niu_board_model_match(np, NIU_QGC_PEM_BM_STR)) || (!strcmp(np->vpd.model, NIU_QGC_PEM_MDL_STR)) ||
(niu_board_model_match(np, NIU_ALONSO_BM_STR))) { (!strcmp(np->vpd.model, NIU_MARAMBA_MDL_STR)) ||
(!strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) ||
(!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR))) {
ports = 4; ports = 4;
} else if ((niu_board_model_match(np, NIU_2XGF_LP_BM_STR)) || } else if ((!strcmp(np->vpd.model, NIU_2XGF_LP_MDL_STR)) ||
(niu_board_model_match(np, NIU_2XGF_PEM_BM_STR)) || (!strcmp(np->vpd.model, NIU_2XGF_PEM_MDL_STR)) ||
(niu_board_model_match(np, NIU_FOXXY_BM_STR)) || (!strcmp(np->vpd.model, NIU_FOXXY_MDL_STR)) ||
(niu_board_model_match(np, NIU_2XGF_MRVL_BM_STR))) { (!strcmp(np->vpd.model, NIU_2XGF_MRVL_MDL_STR))) {
ports = 2; ports = 2;
} }
@ -7053,8 +7036,8 @@ static void __devinit niu_pci_vpd_validate(struct niu *np)
return; return;
} }
if (!strcmp(np->vpd.model, "SUNW,CP3220") || if (!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR) ||
!strcmp(np->vpd.model, "SUNW,CP3260")) { !strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) {
np->flags |= NIU_FLAGS_10G; np->flags |= NIU_FLAGS_10G;
np->flags &= ~NIU_FLAGS_FIBER; np->flags &= ~NIU_FLAGS_FIBER;
np->flags |= NIU_FLAGS_XCVR_SERDES; np->flags |= NIU_FLAGS_XCVR_SERDES;
@ -7065,7 +7048,7 @@ static void __devinit niu_pci_vpd_validate(struct niu *np)
} }
if (np->flags & NIU_FLAGS_10G) if (np->flags & NIU_FLAGS_10G)
np->mac_xcvr = MAC_XCVR_XPCS; np->mac_xcvr = MAC_XCVR_XPCS;
} else if (niu_board_model_match(np, NIU_FOXXY_BM_STR)) { } else if (!strcmp(np->vpd.model, NIU_FOXXY_MDL_STR)) {
np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER | np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER |
NIU_FLAGS_HOTPLUG_PHY); NIU_FLAGS_HOTPLUG_PHY);
} else if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { } else if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) {
@ -7541,8 +7524,8 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent)
u32 val; u32 val;
int err; int err;
if (!strcmp(np->vpd.model, "SUNW,CP3220") || if (!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR) ||
!strcmp(np->vpd.model, "SUNW,CP3260")) { !strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) {
num_10g = 0; num_10g = 0;
num_1g = 2; num_1g = 2;
parent->plat_type = PLAT_TYPE_ATCA_CP3220; parent->plat_type = PLAT_TYPE_ATCA_CP3220;
@ -7551,7 +7534,7 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent)
phy_encode(PORT_TYPE_1G, 1) | phy_encode(PORT_TYPE_1G, 1) |
phy_encode(PORT_TYPE_1G, 2) | phy_encode(PORT_TYPE_1G, 2) |
phy_encode(PORT_TYPE_1G, 3)); phy_encode(PORT_TYPE_1G, 3));
} else if (niu_board_model_match(np, NIU_FOXXY_BM_STR)) { } else if (!strcmp(np->vpd.model, NIU_FOXXY_MDL_STR)) {
num_10g = 2; num_10g = 2;
num_1g = 0; num_1g = 0;
parent->num_ports = 2; parent->num_ports = 2;
@ -7946,6 +7929,7 @@ static int __devinit niu_get_of_props(struct niu *np)
struct device_node *dp; struct device_node *dp;
const char *phy_type; const char *phy_type;
const u8 *mac_addr; const u8 *mac_addr;
const char *model;
int prop_len; int prop_len;
if (np->parent->plat_type == PLAT_TYPE_NIU) if (np->parent->plat_type == PLAT_TYPE_NIU)
@ -8000,6 +7984,11 @@ static int __devinit niu_get_of_props(struct niu *np)
memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len); memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
model = of_get_property(dp, "model", &prop_len);
if (model)
strcpy(np->vpd.model, model);
return 0; return 0;
#else #else
return -EINVAL; return -EINVAL;

View File

@ -2946,6 +2946,15 @@ struct rx_ring_info {
#define NIU_ALONSO_BM_STR "373-0202" #define NIU_ALONSO_BM_STR "373-0202"
#define NIU_FOXXY_BM_STR "501-7961" #define NIU_FOXXY_BM_STR "501-7961"
#define NIU_2XGF_MRVL_BM_STR "SK-6E82" #define NIU_2XGF_MRVL_BM_STR "SK-6E82"
#define NIU_QGC_LP_MDL_STR "SUNW,pcie-qgc"
#define NIU_2XGF_LP_MDL_STR "SUNW,pcie-2xgf"
#define NIU_QGC_PEM_MDL_STR "SUNW,pcie-qgc-pem"
#define NIU_2XGF_PEM_MDL_STR "SUNW,pcie-2xgf-pem"
#define NIU_ALONSO_MDL_STR "SUNW,CP3220"
#define NIU_KIMI_MDL_STR "SUNW,CP3260"
#define NIU_MARAMBA_MDL_STR "SUNW,pcie-neptune"
#define NIU_FOXXY_MDL_STR "SUNW,pcie-rfem"
#define NIU_2XGF_MRVL_MDL_STR "SysKonnect,pcie-2xgf"
#define NIU_VPD_MIN_MAJOR 3 #define NIU_VPD_MIN_MAJOR 3
#define NIU_VPD_MIN_MINOR 4 #define NIU_VPD_MIN_MINOR 4

View File

@ -2458,6 +2458,7 @@ ppp_create_interface(int unit, int *retp)
out3: out3:
atomic_dec(&ppp_unit_count); atomic_dec(&ppp_unit_count);
unregister_netdev(dev);
out2: out2:
mutex_unlock(&all_ppp_mutex); mutex_unlock(&all_ppp_mutex);
free_netdev(dev); free_netdev(dev);

View File

@ -1621,9 +1621,16 @@ out_no_ppp:
end: end:
release_sock(sk); release_sock(sk);
if (error != 0) if (error != 0) {
PRINTK(session ? session->debug : -1, PPPOL2TP_MSG_CONTROL, KERN_WARNING, if (session)
"%s: connect failed: %d\n", session->name, error); PRINTK(session->debug,
PPPOL2TP_MSG_CONTROL, KERN_WARNING,
"%s: connect failed: %d\n",
session->name, error);
else
PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_WARNING,
"connect failed: %d\n", error);
}
return error; return error;
} }

View File

@ -2474,6 +2474,8 @@ static void gelic_wl_free(struct gelic_wl_info *wl)
pr_debug("%s: <-\n", __func__); pr_debug("%s: <-\n", __func__);
free_page((unsigned long)wl->buf);
pr_debug("%s: destroy queues\n", __func__); pr_debug("%s: destroy queues\n", __func__);
destroy_workqueue(wl->eurus_cmd_queue); destroy_workqueue(wl->eurus_cmd_queue);
destroy_workqueue(wl->event_queue); destroy_workqueue(wl->event_queue);

View File

@ -1,5 +1,5 @@
sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \ sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \
i2c-direct.o ethtool.o xfp_phy.o mdio_10g.o \ i2c-direct.o selftest.o ethtool.o xfp_phy.o \
tenxpress.o boards.o sfe4001.o mdio_10g.o tenxpress.o boards.o sfe4001.o
obj-$(CONFIG_SFC) += sfc.o obj-$(CONFIG_SFC) += sfc.o

View File

@ -22,5 +22,7 @@ enum efx_board_type {
extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info); extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info);
extern int sfe4001_poweron(struct efx_nic *efx); extern int sfe4001_poweron(struct efx_nic *efx);
extern void sfe4001_poweroff(struct efx_nic *efx); extern void sfe4001_poweroff(struct efx_nic *efx);
/* Are we putting the PHY into flash config mode */
extern unsigned int sfe4001_phy_flash_cfg;
#endif #endif

View File

@ -1873,6 +1873,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
tx_queue->queue = i; tx_queue->queue = i;
tx_queue->buffer = NULL; tx_queue->buffer = NULL;
tx_queue->channel = &efx->channel[0]; /* for safety */ tx_queue->channel = &efx->channel[0]; /* for safety */
tx_queue->tso_headers_free = NULL;
} }
for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { for (i = 0; i < EFX_MAX_RX_QUEUES; i++) {
rx_queue = &efx->rx_queue[i]; rx_queue = &efx->rx_queue[i];
@ -2071,7 +2072,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
net_dev = alloc_etherdev(sizeof(*efx)); net_dev = alloc_etherdev(sizeof(*efx));
if (!net_dev) if (!net_dev)
return -ENOMEM; return -ENOMEM;
net_dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA; net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO);
if (lro) if (lro)
net_dev->features |= NETIF_F_LRO; net_dev->features |= NETIF_F_LRO;
efx = net_dev->priv; efx = net_dev->priv;

View File

@ -10,6 +10,55 @@
#ifndef EFX_ENUM_H #ifndef EFX_ENUM_H
#define EFX_ENUM_H #define EFX_ENUM_H
/**
* enum efx_loopback_mode - loopback modes
* @LOOPBACK_NONE: no loopback
* @LOOPBACK_XGMII: loopback within MAC at XGMII level
* @LOOPBACK_XGXS: loopback within MAC at XGXS level
* @LOOPBACK_XAUI: loopback within MAC at XAUI level
* @LOOPBACK_PHYXS: loopback within PHY at PHYXS level
* @LOOPBACK_PCS: loopback within PHY at PCS level
* @LOOPBACK_PMAPMD: loopback within PHY at PMAPMD level
* @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!)
*/
/* Please keep in order and up-to-date w.r.t the following two #defines */
enum efx_loopback_mode {
LOOPBACK_NONE = 0,
LOOPBACK_MAC = 1,
LOOPBACK_XGMII = 2,
LOOPBACK_XGXS = 3,
LOOPBACK_XAUI = 4,
LOOPBACK_PHY = 5,
LOOPBACK_PHYXS = 6,
LOOPBACK_PCS = 7,
LOOPBACK_PMAPMD = 8,
LOOPBACK_NETWORK = 9,
LOOPBACK_MAX
};
#define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD
extern const char *efx_loopback_mode_names[];
#define LOOPBACK_MODE_NAME(mode) \
STRING_TABLE_LOOKUP(mode, efx_loopback_mode)
#define LOOPBACK_MODE(efx) \
LOOPBACK_MODE_NAME(efx->loopback_mode)
/* These loopbacks occur within the controller */
#define LOOPBACKS_10G_INTERNAL ((1 << LOOPBACK_XGMII)| \
(1 << LOOPBACK_XGXS) | \
(1 << LOOPBACK_XAUI))
#define LOOPBACK_MASK(_efx) \
(1 << (_efx)->loopback_mode)
#define LOOPBACK_INTERNAL(_efx) \
((LOOPBACKS_10G_INTERNAL & LOOPBACK_MASK(_efx)) ? 1 : 0)
#define LOOPBACK_OUT_OF(_from, _to, _mask) \
(((LOOPBACK_MASK(_from) & (_mask)) && \
((LOOPBACK_MASK(_to) & (_mask)) == 0)) ? 1 : 0)
/*****************************************************************************/ /*****************************************************************************/
/** /**

View File

@ -12,12 +12,26 @@
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include "net_driver.h" #include "net_driver.h"
#include "selftest.h"
#include "efx.h" #include "efx.h"
#include "ethtool.h" #include "ethtool.h"
#include "falcon.h" #include "falcon.h"
#include "gmii.h" #include "gmii.h"
#include "mac.h" #include "mac.h"
const char *efx_loopback_mode_names[] = {
[LOOPBACK_NONE] = "NONE",
[LOOPBACK_MAC] = "MAC",
[LOOPBACK_XGMII] = "XGMII",
[LOOPBACK_XGXS] = "XGXS",
[LOOPBACK_XAUI] = "XAUI",
[LOOPBACK_PHY] = "PHY",
[LOOPBACK_PHYXS] = "PHY(XS)",
[LOOPBACK_PCS] = "PHY(PCS)",
[LOOPBACK_PMAPMD] = "PHY(PMAPMD)",
[LOOPBACK_NETWORK] = "NETWORK",
};
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable);
struct ethtool_string { struct ethtool_string {
@ -217,23 +231,179 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
} }
/**
* efx_fill_test - fill in an individual self-test entry
* @test_index: Index of the test
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
* @test: Pointer to test result (used only if data != %NULL)
* @unit_format: Unit name format (e.g. "channel\%d")
* @unit_id: Unit id (e.g. 0 for "channel0")
* @test_format: Test name format (e.g. "loopback.\%s.tx.sent")
* @test_id: Test id (e.g. "PHY" for "loopback.PHY.tx_sent")
*
* Fill in an individual self-test entry.
*/
static void efx_fill_test(unsigned int test_index,
struct ethtool_string *strings, u64 *data,
int *test, const char *unit_format, int unit_id,
const char *test_format, const char *test_id)
{
struct ethtool_string unit_str, test_str;
/* Fill data value, if applicable */
if (data)
data[test_index] = *test;
/* Fill string, if applicable */
if (strings) {
snprintf(unit_str.name, sizeof(unit_str.name),
unit_format, unit_id);
snprintf(test_str.name, sizeof(test_str.name),
test_format, test_id);
snprintf(strings[test_index].name,
sizeof(strings[test_index].name),
"%-9s%-17s", unit_str.name, test_str.name);
}
}
#define EFX_PORT_NAME "port%d", 0
#define EFX_CHANNEL_NAME(_channel) "channel%d", _channel->channel
#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
#define EFX_LOOPBACK_NAME(_mode, _counter) \
"loopback.%s." _counter, LOOPBACK_MODE_NAME(mode)
/**
* efx_fill_loopback_test - fill in a block of loopback self-test entries
* @efx: Efx NIC
* @lb_tests: Efx loopback self-test results structure
* @mode: Loopback test mode
* @test_index: Starting index of the test
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
*/
static int efx_fill_loopback_test(struct efx_nic *efx,
struct efx_loopback_self_tests *lb_tests,
enum efx_loopback_mode mode,
unsigned int test_index,
struct ethtool_string *strings, u64 *data)
{
struct efx_tx_queue *tx_queue;
efx_for_each_tx_queue(tx_queue, efx) {
efx_fill_test(test_index++, strings, data,
&lb_tests->tx_sent[tx_queue->queue],
EFX_TX_QUEUE_NAME(tx_queue),
EFX_LOOPBACK_NAME(mode, "tx_sent"));
efx_fill_test(test_index++, strings, data,
&lb_tests->tx_done[tx_queue->queue],
EFX_TX_QUEUE_NAME(tx_queue),
EFX_LOOPBACK_NAME(mode, "tx_done"));
}
efx_fill_test(test_index++, strings, data,
&lb_tests->rx_good,
EFX_PORT_NAME,
EFX_LOOPBACK_NAME(mode, "rx_good"));
efx_fill_test(test_index++, strings, data,
&lb_tests->rx_bad,
EFX_PORT_NAME,
EFX_LOOPBACK_NAME(mode, "rx_bad"));
return test_index;
}
/**
* efx_ethtool_fill_self_tests - get self-test details
* @efx: Efx NIC
* @tests: Efx self-test results structure, or %NULL
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
*/
static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
struct efx_self_tests *tests,
struct ethtool_string *strings,
u64 *data)
{
struct efx_channel *channel;
unsigned int n = 0;
enum efx_loopback_mode mode;
/* Interrupt */
efx_fill_test(n++, strings, data, &tests->interrupt,
"core", 0, "interrupt", NULL);
/* Event queues */
efx_for_each_channel(channel, efx) {
efx_fill_test(n++, strings, data,
&tests->eventq_dma[channel->channel],
EFX_CHANNEL_NAME(channel),
"eventq.dma", NULL);
efx_fill_test(n++, strings, data,
&tests->eventq_int[channel->channel],
EFX_CHANNEL_NAME(channel),
"eventq.int", NULL);
efx_fill_test(n++, strings, data,
&tests->eventq_poll[channel->channel],
EFX_CHANNEL_NAME(channel),
"eventq.poll", NULL);
}
/* PHY presence */
efx_fill_test(n++, strings, data, &tests->phy_ok,
EFX_PORT_NAME, "phy_ok", NULL);
/* Loopback tests */
efx_fill_test(n++, strings, data, &tests->loopback_speed,
EFX_PORT_NAME, "loopback.speed", NULL);
efx_fill_test(n++, strings, data, &tests->loopback_full_duplex,
EFX_PORT_NAME, "loopback.full_duplex", NULL);
for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) {
if (!(efx->loopback_modes & (1 << mode)))
continue;
n = efx_fill_loopback_test(efx,
&tests->loopback[mode], mode, n,
strings, data);
}
return n;
}
static int efx_ethtool_get_stats_count(struct net_device *net_dev) static int efx_ethtool_get_stats_count(struct net_device *net_dev)
{ {
return EFX_ETHTOOL_NUM_STATS; return EFX_ETHTOOL_NUM_STATS;
} }
static int efx_ethtool_self_test_count(struct net_device *net_dev)
{
struct efx_nic *efx = net_dev->priv;
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
}
static void efx_ethtool_get_strings(struct net_device *net_dev, static void efx_ethtool_get_strings(struct net_device *net_dev,
u32 string_set, u8 *strings) u32 string_set, u8 *strings)
{ {
struct efx_nic *efx = net_dev->priv;
struct ethtool_string *ethtool_strings = struct ethtool_string *ethtool_strings =
(struct ethtool_string *)strings; (struct ethtool_string *)strings;
int i; int i;
if (string_set == ETH_SS_STATS) switch (string_set) {
case ETH_SS_STATS:
for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++)
strncpy(ethtool_strings[i].name, strncpy(ethtool_strings[i].name,
efx_ethtool_stats[i].name, efx_ethtool_stats[i].name,
sizeof(ethtool_strings[i].name)); sizeof(ethtool_strings[i].name));
break;
case ETH_SS_TEST:
efx_ethtool_fill_self_tests(efx, NULL,
ethtool_strings, NULL);
break;
default:
/* No other string sets */
break;
}
} }
static void efx_ethtool_get_stats(struct net_device *net_dev, static void efx_ethtool_get_stats(struct net_device *net_dev,
@ -272,6 +442,22 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
} }
} }
static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable)
{
int rc;
/* Our TSO requires TX checksumming, so force TX checksumming
* on when TSO is enabled.
*/
if (enable) {
rc = efx_ethtool_set_tx_csum(net_dev, 1);
if (rc)
return rc;
}
return ethtool_op_set_tso(net_dev, enable);
}
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
{ {
struct efx_nic *efx = net_dev->priv; struct efx_nic *efx = net_dev->priv;
@ -283,6 +469,15 @@ static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
efx_flush_queues(efx); efx_flush_queues(efx);
/* Our TSO requires TX checksumming, so disable TSO when
* checksumming is disabled
*/
if (!enable) {
rc = efx_ethtool_set_tso(net_dev, 0);
if (rc)
return rc;
}
return 0; return 0;
} }
@ -305,6 +500,64 @@ static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev)
return efx->rx_checksum_enabled; return efx->rx_checksum_enabled;
} }
static void efx_ethtool_self_test(struct net_device *net_dev,
struct ethtool_test *test, u64 *data)
{
struct efx_nic *efx = net_dev->priv;
struct efx_self_tests efx_tests;
int offline, already_up;
int rc;
ASSERT_RTNL();
if (efx->state != STATE_RUNNING) {
rc = -EIO;
goto fail1;
}
/* We need rx buffers and interrupts. */
already_up = (efx->net_dev->flags & IFF_UP);
if (!already_up) {
rc = dev_open(efx->net_dev);
if (rc) {
EFX_ERR(efx, "failed opening device.\n");
goto fail2;
}
}
memset(&efx_tests, 0, sizeof(efx_tests));
offline = (test->flags & ETH_TEST_FL_OFFLINE);
/* Perform online self tests first */
rc = efx_online_test(efx, &efx_tests);
if (rc)
goto out;
/* Perform offline tests only if online tests passed */
if (offline) {
/* Stop the kernel from sending packets during the test. */
efx_stop_queue(efx);
rc = efx_flush_queues(efx);
if (!rc)
rc = efx_offline_test(efx, &efx_tests,
efx->loopback_modes);
efx_wake_queue(efx);
}
out:
if (!already_up)
dev_close(efx->net_dev);
EFX_LOG(efx, "%s all %sline self-tests\n",
rc == 0 ? "passed" : "failed", offline ? "off" : "on");
fail2:
fail1:
/* Fill ethtool results structures */
efx_ethtool_fill_self_tests(efx, &efx_tests, NULL, data);
if (rc)
test->flags |= ETH_TEST_FL_FAILED;
}
/* Restart autonegotiation */ /* Restart autonegotiation */
static int efx_ethtool_nway_reset(struct net_device *net_dev) static int efx_ethtool_nway_reset(struct net_device *net_dev)
{ {
@ -451,8 +704,12 @@ struct ethtool_ops efx_ethtool_ops = {
.set_tx_csum = efx_ethtool_set_tx_csum, .set_tx_csum = efx_ethtool_set_tx_csum,
.get_sg = ethtool_op_get_sg, .get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg, .set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = efx_ethtool_set_tso,
.get_flags = ethtool_op_get_flags, .get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags, .set_flags = ethtool_op_set_flags,
.self_test_count = efx_ethtool_self_test_count,
.self_test = efx_ethtool_self_test,
.get_strings = efx_ethtool_get_strings, .get_strings = efx_ethtool_get_strings,
.phys_id = efx_ethtool_phys_id, .phys_id = efx_ethtool_phys_id,
.get_stats_count = efx_ethtool_get_stats_count, .get_stats_count = efx_ethtool_get_stats_count,

View File

@ -1129,6 +1129,7 @@ static void falcon_handle_driver_event(struct efx_channel *channel,
case RX_RECOVERY_EV_DECODE: case RX_RECOVERY_EV_DECODE:
EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. "
"Resetting.\n", channel->channel); "Resetting.\n", channel->channel);
atomic_inc(&efx->rx_reset);
efx_schedule_reset(efx, efx_schedule_reset(efx,
EFX_WORKAROUND_6555(efx) ? EFX_WORKAROUND_6555(efx) ?
RESET_TYPE_RX_RECOVERY : RESET_TYPE_RX_RECOVERY :
@ -1731,7 +1732,8 @@ void falcon_drain_tx_fifo(struct efx_nic *efx)
efx_oword_t temp; efx_oword_t temp;
int count; int count;
if (FALCON_REV(efx) < FALCON_REV_B0) if ((FALCON_REV(efx) < FALCON_REV_B0) ||
(efx->loopback_mode != LOOPBACK_NONE))
return; return;
falcon_read(efx, &temp, MAC0_CTRL_REG_KER); falcon_read(efx, &temp, MAC0_CTRL_REG_KER);
@ -2091,6 +2093,8 @@ static int falcon_probe_phy(struct efx_nic *efx)
efx->phy_type); efx->phy_type);
return -1; return -1;
} }
efx->loopback_modes = LOOPBACKS_10G_INTERNAL | efx->phy_op->loopbacks;
return 0; return 0;
} }
@ -2468,14 +2472,12 @@ int falcon_probe_nic(struct efx_nic *efx)
fail5: fail5:
falcon_free_buffer(efx, &efx->irq_status); falcon_free_buffer(efx, &efx->irq_status);
fail4: fail4:
/* fall-thru */
fail3: fail3:
if (nic_data->pci_dev2) { if (nic_data->pci_dev2) {
pci_dev_put(nic_data->pci_dev2); pci_dev_put(nic_data->pci_dev2);
nic_data->pci_dev2 = NULL; nic_data->pci_dev2 = NULL;
} }
fail2: fail2:
/* fall-thru */
fail1: fail1:
kfree(efx->nic_data); kfree(efx->nic_data);
return rc; return rc;

View File

@ -636,6 +636,14 @@
#define XX_HIDRVA_WIDTH 1 #define XX_HIDRVA_WIDTH 1
#define XX_LODRVA_LBN 8 #define XX_LODRVA_LBN 8
#define XX_LODRVA_WIDTH 1 #define XX_LODRVA_WIDTH 1
#define XX_LPBKD_LBN 3
#define XX_LPBKD_WIDTH 1
#define XX_LPBKC_LBN 2
#define XX_LPBKC_WIDTH 1
#define XX_LPBKB_LBN 1
#define XX_LPBKB_WIDTH 1
#define XX_LPBKA_LBN 0
#define XX_LPBKA_WIDTH 1
#define XX_TXDRV_CTL_REG_MAC 0x12 #define XX_TXDRV_CTL_REG_MAC 0x12
#define XX_DEQD_LBN 28 #define XX_DEQD_LBN 28
@ -656,8 +664,14 @@
#define XX_DTXA_WIDTH 4 #define XX_DTXA_WIDTH 4
/* XAUI XGXS core status register */ /* XAUI XGXS core status register */
#define XX_FORCE_SIG_DECODE_FORCED 0xff
#define XX_CORE_STAT_REG_MAC 0x16 #define XX_CORE_STAT_REG_MAC 0x16
#define XX_FORCE_SIG_LBN 24
#define XX_FORCE_SIG_WIDTH 8
#define XX_FORCE_SIG_DECODE_FORCED 0xff
#define XX_XGXS_LB_EN_LBN 23
#define XX_XGXS_LB_EN_WIDTH 1
#define XX_XGMII_LB_EN_LBN 22
#define XX_XGMII_LB_EN_WIDTH 1
#define XX_ALIGN_DONE_LBN 20 #define XX_ALIGN_DONE_LBN 20
#define XX_ALIGN_DONE_WIDTH 1 #define XX_ALIGN_DONE_WIDTH 1
#define XX_SYNC_STAT_LBN 16 #define XX_SYNC_STAT_LBN 16

View File

@ -32,7 +32,7 @@
(FALCON_XMAC_REGBANK + ((mac_reg) * FALCON_XMAC_REG_SIZE)) (FALCON_XMAC_REGBANK + ((mac_reg) * FALCON_XMAC_REG_SIZE))
void falcon_xmac_writel(struct efx_nic *efx, void falcon_xmac_writel(struct efx_nic *efx,
efx_dword_t *value, unsigned int mac_reg) efx_dword_t *value, unsigned int mac_reg)
{ {
efx_oword_t temp; efx_oword_t temp;
@ -69,6 +69,10 @@ static int falcon_reset_xmac(struct efx_nic *efx)
udelay(10); udelay(10);
} }
/* This often fails when DSP is disabled, ignore it */
if (sfe4001_phy_flash_cfg != 0)
return 0;
EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
return -ETIMEDOUT; return -ETIMEDOUT;
} }
@ -223,7 +227,7 @@ static int falcon_xgmii_status(struct efx_nic *efx)
/* The ISR latches, so clear it and re-read */ /* The ISR latches, so clear it and re-read */
falcon_xmac_readl(efx, &reg, XM_MGT_INT_REG_MAC_B0); falcon_xmac_readl(efx, &reg, XM_MGT_INT_REG_MAC_B0);
falcon_xmac_readl(efx, &reg, XM_MGT_INT_REG_MAC_B0); falcon_xmac_readl(efx, &reg, XM_MGT_INT_REG_MAC_B0);
if (EFX_DWORD_FIELD(reg, XM_LCLFLT) || if (EFX_DWORD_FIELD(reg, XM_LCLFLT) ||
EFX_DWORD_FIELD(reg, XM_RMTFLT)) { EFX_DWORD_FIELD(reg, XM_RMTFLT)) {
EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg)); EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg));
@ -237,7 +241,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, int enable)
{ {
efx_dword_t reg; efx_dword_t reg;
if (FALCON_REV(efx) < FALCON_REV_B0) if ((FALCON_REV(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
return; return;
/* Flush the ISR */ /* Flush the ISR */
@ -284,6 +288,9 @@ int falcon_xaui_link_ok(struct efx_nic *efx)
efx_dword_t reg; efx_dword_t reg;
int align_done, sync_status, link_ok = 0; int align_done, sync_status, link_ok = 0;
if (LOOPBACK_INTERNAL(efx))
return 1;
/* Read link status */ /* Read link status */
falcon_xmac_readl(efx, &reg, XX_CORE_STAT_REG_MAC); falcon_xmac_readl(efx, &reg, XX_CORE_STAT_REG_MAC);
@ -374,6 +381,61 @@ static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
falcon_xmac_writel(efx, &reg, XM_ADR_HI_REG_MAC); falcon_xmac_writel(efx, &reg, XM_ADR_HI_REG_MAC);
} }
static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
{
efx_dword_t reg;
int xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS) ? 1 : 0;
int xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI) ? 1 : 0;
int xgmii_loopback =
(efx->loopback_mode == LOOPBACK_XGMII) ? 1 : 0;
/* XGXS block is flaky and will need to be reset if moving
* into our out of XGMII, XGXS or XAUI loopbacks. */
if (EFX_WORKAROUND_5147(efx)) {
int old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback;
int reset_xgxs;
falcon_xmac_readl(efx, &reg, XX_CORE_STAT_REG_MAC);
old_xgxs_loopback = EFX_DWORD_FIELD(reg, XX_XGXS_LB_EN);
old_xgmii_loopback = EFX_DWORD_FIELD(reg, XX_XGMII_LB_EN);
falcon_xmac_readl(efx, &reg, XX_SD_CTL_REG_MAC);
old_xaui_loopback = EFX_DWORD_FIELD(reg, XX_LPBKA);
/* The PHY driver may have turned XAUI off */
reset_xgxs = ((xgxs_loopback != old_xgxs_loopback) ||
(xaui_loopback != old_xaui_loopback) ||
(xgmii_loopback != old_xgmii_loopback));
if (reset_xgxs) {
falcon_xmac_readl(efx, &reg, XX_PWR_RST_REG_MAC);
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 1);
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 1);
falcon_xmac_writel(efx, &reg, XX_PWR_RST_REG_MAC);
udelay(1);
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 0);
EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 0);
falcon_xmac_writel(efx, &reg, XX_PWR_RST_REG_MAC);
udelay(1);
}
}
falcon_xmac_readl(efx, &reg, XX_CORE_STAT_REG_MAC);
EFX_SET_DWORD_FIELD(reg, XX_FORCE_SIG,
(xgxs_loopback || xaui_loopback) ?
XX_FORCE_SIG_DECODE_FORCED : 0);
EFX_SET_DWORD_FIELD(reg, XX_XGXS_LB_EN, xgxs_loopback);
EFX_SET_DWORD_FIELD(reg, XX_XGMII_LB_EN, xgmii_loopback);
falcon_xmac_writel(efx, &reg, XX_CORE_STAT_REG_MAC);
falcon_xmac_readl(efx, &reg, XX_SD_CTL_REG_MAC);
EFX_SET_DWORD_FIELD(reg, XX_LPBKD, xaui_loopback);
EFX_SET_DWORD_FIELD(reg, XX_LPBKC, xaui_loopback);
EFX_SET_DWORD_FIELD(reg, XX_LPBKB, xaui_loopback);
EFX_SET_DWORD_FIELD(reg, XX_LPBKA, xaui_loopback);
falcon_xmac_writel(efx, &reg, XX_SD_CTL_REG_MAC);
}
/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails /* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
* to come back up. Bash it until it comes back up */ * to come back up. Bash it until it comes back up */
static int falcon_check_xaui_link_up(struct efx_nic *efx) static int falcon_check_xaui_link_up(struct efx_nic *efx)
@ -382,7 +444,8 @@ static int falcon_check_xaui_link_up(struct efx_nic *efx)
tries = EFX_WORKAROUND_5147(efx) ? 5 : 1; tries = EFX_WORKAROUND_5147(efx) ? 5 : 1;
max_tries = tries; max_tries = tries;
if (efx->phy_type == PHY_TYPE_NONE) if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
(efx->phy_type == PHY_TYPE_NONE))
return 0; return 0;
while (tries) { while (tries) {
@ -408,8 +471,13 @@ void falcon_reconfigure_xmac(struct efx_nic *efx)
falcon_mask_status_intr(efx, 0); falcon_mask_status_intr(efx, 0);
falcon_deconfigure_mac_wrapper(efx); falcon_deconfigure_mac_wrapper(efx);
efx->tx_disabled = LOOPBACK_INTERNAL(efx);
efx->phy_op->reconfigure(efx); efx->phy_op->reconfigure(efx);
falcon_reconfigure_xgxs_core(efx);
falcon_reconfigure_xmac_core(efx); falcon_reconfigure_xmac_core(efx);
falcon_reconfigure_mac_wrapper(efx); falcon_reconfigure_mac_wrapper(efx);
/* Ensure XAUI link is up */ /* Ensure XAUI link is up */
@ -491,13 +559,15 @@ void falcon_update_stats_xmac(struct efx_nic *efx)
(mac_stats->rx_bytes - mac_stats->rx_good_bytes); (mac_stats->rx_bytes - mac_stats->rx_good_bytes);
} }
#define EFX_XAUI_RETRAIN_MAX 8
int falcon_check_xmac(struct efx_nic *efx) int falcon_check_xmac(struct efx_nic *efx)
{ {
unsigned xaui_link_ok; unsigned xaui_link_ok;
int rc; int rc;
if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
(efx->phy_type == PHY_TYPE_NONE))
return 0;
falcon_mask_status_intr(efx, 0); falcon_mask_status_intr(efx, 0);
xaui_link_ok = falcon_xaui_link_ok(efx); xaui_link_ok = falcon_xaui_link_ok(efx);

View File

@ -44,6 +44,9 @@ static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd,
int status; int status;
int phy_id = efx->mii.phy_id; int phy_id = efx->mii.phy_id;
if (LOOPBACK_INTERNAL(efx))
return 0;
/* Read MMD STATUS2 to check it is responding. */ /* Read MMD STATUS2 to check it is responding. */
status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2); status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2);
if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
@ -164,6 +167,22 @@ int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
int mmd = 0; int mmd = 0;
int good; int good;
/* If the port is in loopback, then we should only consider a subset
* of mmd's */
if (LOOPBACK_INTERNAL(efx))
return 1;
else if (efx->loopback_mode == LOOPBACK_NETWORK)
return 0;
else if (efx->loopback_mode == LOOPBACK_PHYXS)
mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS |
MDIO_MMDREG_DEVS0_PCS |
MDIO_MMDREG_DEVS0_PMAPMD);
else if (efx->loopback_mode == LOOPBACK_PCS)
mmd_mask &= ~(MDIO_MMDREG_DEVS0_PCS |
MDIO_MMDREG_DEVS0_PMAPMD);
else if (efx->loopback_mode == LOOPBACK_PMAPMD)
mmd_mask &= ~MDIO_MMDREG_DEVS0_PMAPMD;
while (mmd_mask) { while (mmd_mask) {
if (mmd_mask & 1) { if (mmd_mask & 1) {
/* Double reads because link state is latched, and a /* Double reads because link state is latched, and a
@ -182,6 +201,65 @@ int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
return ok; return ok;
} }
void mdio_clause45_transmit_disable(struct efx_nic *efx)
{
int phy_id = efx->mii.phy_id;
int ctrl1, ctrl2;
ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
MDIO_MMDREG_TXDIS);
if (efx->tx_disabled)
ctrl2 |= (1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
else
ctrl1 &= ~(1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
if (ctrl1 != ctrl2)
mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
MDIO_MMDREG_TXDIS, ctrl2);
}
void mdio_clause45_phy_reconfigure(struct efx_nic *efx)
{
int phy_id = efx->mii.phy_id;
int ctrl1, ctrl2;
/* Handle (with debouncing) PMA/PMD loopback */
ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
MDIO_MMDREG_CTRL1);
if (efx->loopback_mode == LOOPBACK_PMAPMD)
ctrl2 |= (1 << MDIO_PMAPMD_CTRL1_LBACK_LBN);
else
ctrl2 &= ~(1 << MDIO_PMAPMD_CTRL1_LBACK_LBN);
if (ctrl1 != ctrl2)
mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
MDIO_MMDREG_CTRL1, ctrl2);
/* Handle (with debouncing) PCS loopback */
ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
MDIO_MMDREG_CTRL1);
if (efx->loopback_mode == LOOPBACK_PCS)
ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
else
ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
if (ctrl1 != ctrl2)
mdio_clause45_write(efx, phy_id, MDIO_MMD_PCS,
MDIO_MMDREG_CTRL1, ctrl2);
/* Handle (with debouncing) PHYXS network loopback */
ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
MDIO_MMDREG_CTRL1);
if (efx->loopback_mode == LOOPBACK_NETWORK)
ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
else
ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
if (ctrl1 != ctrl2)
mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS,
MDIO_MMDREG_CTRL1, ctrl2);
}
/** /**
* mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO. * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
* @efx: Efx NIC * @efx: Efx NIC

View File

@ -44,11 +44,16 @@
#define MDIO_MMDREG_DEVS1 (6) #define MDIO_MMDREG_DEVS1 (6)
#define MDIO_MMDREG_CTRL2 (7) #define MDIO_MMDREG_CTRL2 (7)
#define MDIO_MMDREG_STAT2 (8) #define MDIO_MMDREG_STAT2 (8)
#define MDIO_MMDREG_TXDIS (9)
/* Bits in MMDREG_CTRL1 */ /* Bits in MMDREG_CTRL1 */
/* Reset */ /* Reset */
#define MDIO_MMDREG_CTRL1_RESET_LBN (15) #define MDIO_MMDREG_CTRL1_RESET_LBN (15)
#define MDIO_MMDREG_CTRL1_RESET_WIDTH (1) #define MDIO_MMDREG_CTRL1_RESET_WIDTH (1)
/* Loopback */
/* Loopback bit for WIS, PCS, PHYSX and DTEXS */
#define MDIO_MMDREG_CTRL1_LBACK_LBN (14)
#define MDIO_MMDREG_CTRL1_LBACK_WIDTH (1)
/* Bits in MMDREG_STAT1 */ /* Bits in MMDREG_STAT1 */
#define MDIO_MMDREG_STAT1_FAULT_LBN (7) #define MDIO_MMDREG_STAT1_FAULT_LBN (7)
@ -56,6 +61,9 @@
/* Link state */ /* Link state */
#define MDIO_MMDREG_STAT1_LINK_LBN (2) #define MDIO_MMDREG_STAT1_LINK_LBN (2)
#define MDIO_MMDREG_STAT1_LINK_WIDTH (1) #define MDIO_MMDREG_STAT1_LINK_WIDTH (1)
/* Low power ability */
#define MDIO_MMDREG_STAT1_LPABLE_LBN (1)
#define MDIO_MMDREG_STAT1_LPABLE_WIDTH (1)
/* Bits in ID reg */ /* Bits in ID reg */
#define MDIO_ID_REV(_id32) (_id32 & 0xf) #define MDIO_ID_REV(_id32) (_id32 & 0xf)
@ -76,6 +84,14 @@
#define MDIO_MMDREG_STAT2_PRESENT_LBN (14) #define MDIO_MMDREG_STAT2_PRESENT_LBN (14)
#define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2) #define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2)
/* Bits in MMDREG_TXDIS */
#define MDIO_MMDREG_TXDIS_GLOBAL_LBN (0)
#define MDIO_MMDREG_TXDIS_GLOBAL_WIDTH (1)
/* MMD-specific bits, ordered by MMD, then register */
#define MDIO_PMAPMD_CTRL1_LBACK_LBN (0)
#define MDIO_PMAPMD_CTRL1_LBACK_WIDTH (1)
/* PMA type (4 bits) */ /* PMA type (4 bits) */
#define MDIO_PMAPMD_CTRL2_10G_CX4 (0x0) #define MDIO_PMAPMD_CTRL2_10G_CX4 (0x0)
#define MDIO_PMAPMD_CTRL2_10G_EW (0x1) #define MDIO_PMAPMD_CTRL2_10G_EW (0x1)
@ -95,7 +111,7 @@
#define MDIO_PMAPMD_CTRL2_10_BT (0xf) #define MDIO_PMAPMD_CTRL2_10_BT (0xf)
#define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf) #define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf)
/* /\* PHY XGXS lane state *\/ */ /* PHY XGXS lane state */
#define MDIO_PHYXS_LANE_STATE (0x18) #define MDIO_PHYXS_LANE_STATE (0x18)
#define MDIO_PHYXS_LANE_ALIGNED_LBN (12) #define MDIO_PHYXS_LANE_ALIGNED_LBN (12)
@ -217,6 +233,12 @@ int mdio_clause45_check_mmds(struct efx_nic *efx,
extern int mdio_clause45_links_ok(struct efx_nic *efx, extern int mdio_clause45_links_ok(struct efx_nic *efx,
unsigned int mmd_mask); unsigned int mmd_mask);
/* Generic transmit disable support though PMAPMD */
extern void mdio_clause45_transmit_disable(struct efx_nic *efx);
/* Generic part of reconfigure: set/clear loopback bits */
extern void mdio_clause45_phy_reconfigure(struct efx_nic *efx);
/* Read (some of) the PHY settings over MDIO */ /* Read (some of) the PHY settings over MDIO */
extern void mdio_clause45_get_settings(struct efx_nic *efx, extern void mdio_clause45_get_settings(struct efx_nic *efx,
struct ethtool_cmd *ecmd); struct ethtool_cmd *ecmd);

View File

@ -134,6 +134,8 @@ struct efx_special_buffer {
* Set only on the final fragment of a packet; %NULL for all other * Set only on the final fragment of a packet; %NULL for all other
* fragments. When this fragment completes, then we can free this * fragments. When this fragment completes, then we can free this
* skb. * skb.
* @tsoh: The associated TSO header structure, or %NULL if this
* buffer is not a TSO header.
* @dma_addr: DMA address of the fragment. * @dma_addr: DMA address of the fragment.
* @len: Length of this fragment. * @len: Length of this fragment.
* This field is zero when the queue slot is empty. * This field is zero when the queue slot is empty.
@ -144,6 +146,7 @@ struct efx_special_buffer {
*/ */
struct efx_tx_buffer { struct efx_tx_buffer {
const struct sk_buff *skb; const struct sk_buff *skb;
struct efx_tso_header *tsoh;
dma_addr_t dma_addr; dma_addr_t dma_addr;
unsigned short len; unsigned short len;
unsigned char continuation; unsigned char continuation;
@ -187,6 +190,13 @@ struct efx_tx_buffer {
* variable indicates that the queue is full. This is to * variable indicates that the queue is full. This is to
* avoid cache-line ping-pong between the xmit path and the * avoid cache-line ping-pong between the xmit path and the
* completion path. * completion path.
* @tso_headers_free: A list of TSO headers allocated for this TX queue
* that are not in use, and so available for new TSO sends. The list
* is protected by the TX queue lock.
* @tso_bursts: Number of times TSO xmit invoked by kernel
* @tso_long_headers: Number of packets with headers too long for standard
* blocks
* @tso_packets: Number of packets via the TSO xmit path
*/ */
struct efx_tx_queue { struct efx_tx_queue {
/* Members which don't change on the fast path */ /* Members which don't change on the fast path */
@ -206,6 +216,10 @@ struct efx_tx_queue {
unsigned int insert_count ____cacheline_aligned_in_smp; unsigned int insert_count ____cacheline_aligned_in_smp;
unsigned int write_count; unsigned int write_count;
unsigned int old_read_count; unsigned int old_read_count;
struct efx_tso_header *tso_headers_free;
unsigned int tso_bursts;
unsigned int tso_long_headers;
unsigned int tso_packets;
}; };
/** /**
@ -434,6 +448,9 @@ struct efx_board {
struct efx_blinker blinker; struct efx_blinker blinker;
}; };
#define STRING_TABLE_LOOKUP(val, member) \
member ## _names[val]
enum efx_int_mode { enum efx_int_mode {
/* Be careful if altering to correct macro below */ /* Be careful if altering to correct macro below */
EFX_INT_MODE_MSIX = 0, EFX_INT_MODE_MSIX = 0,
@ -506,6 +523,7 @@ enum efx_fc_type {
* @check_hw: Check hardware * @check_hw: Check hardware
* @reset_xaui: Reset XAUI side of PHY for (software sequenced reset) * @reset_xaui: Reset XAUI side of PHY for (software sequenced reset)
* @mmds: MMD presence mask * @mmds: MMD presence mask
* @loopbacks: Supported loopback modes mask
*/ */
struct efx_phy_operations { struct efx_phy_operations {
int (*init) (struct efx_nic *efx); int (*init) (struct efx_nic *efx);
@ -515,6 +533,7 @@ struct efx_phy_operations {
int (*check_hw) (struct efx_nic *efx); int (*check_hw) (struct efx_nic *efx);
void (*reset_xaui) (struct efx_nic *efx); void (*reset_xaui) (struct efx_nic *efx);
int mmds; int mmds;
unsigned loopbacks;
}; };
/* /*
@ -653,7 +672,6 @@ union efx_multicast_hash {
* @phy_op: PHY interface * @phy_op: PHY interface
* @phy_data: PHY private data (including PHY-specific stats) * @phy_data: PHY private data (including PHY-specific stats)
* @mii: PHY interface * @mii: PHY interface
* @phy_powered: PHY power state
* @tx_disabled: PHY transmitter turned off * @tx_disabled: PHY transmitter turned off
* @link_up: Link status * @link_up: Link status
* @link_options: Link options (MII/GMII format) * @link_options: Link options (MII/GMII format)
@ -662,6 +680,9 @@ union efx_multicast_hash {
* @multicast_hash: Multicast hash table * @multicast_hash: Multicast hash table
* @flow_control: Flow control flags - separate RX/TX so can't use link_options * @flow_control: Flow control flags - separate RX/TX so can't use link_options
* @reconfigure_work: work item for dealing with PHY events * @reconfigure_work: work item for dealing with PHY events
* @loopback_mode: Loopback status
* @loopback_modes: Supported loopback mode bitmask
* @loopback_selftest: Offline self-test private state
* *
* The @priv field of the corresponding &struct net_device points to * The @priv field of the corresponding &struct net_device points to
* this. * this.
@ -721,6 +742,7 @@ struct efx_nic {
struct efx_phy_operations *phy_op; struct efx_phy_operations *phy_op;
void *phy_data; void *phy_data;
struct mii_if_info mii; struct mii_if_info mii;
unsigned tx_disabled;
int link_up; int link_up;
unsigned int link_options; unsigned int link_options;
@ -732,6 +754,10 @@ struct efx_nic {
struct work_struct reconfigure_work; struct work_struct reconfigure_work;
atomic_t rx_reset; atomic_t rx_reset;
enum efx_loopback_mode loopback_mode;
unsigned int loopback_modes;
void *loopback_selftest;
}; };
/** /**

View File

@ -19,6 +19,7 @@
#include "rx.h" #include "rx.h"
#include "efx.h" #include "efx.h"
#include "falcon.h" #include "falcon.h"
#include "selftest.h"
#include "workarounds.h" #include "workarounds.h"
/* Number of RX descriptors pushed at once. */ /* Number of RX descriptors pushed at once. */
@ -683,6 +684,15 @@ void __efx_rx_packet(struct efx_channel *channel,
struct sk_buff *skb; struct sk_buff *skb;
int lro = efx->net_dev->features & NETIF_F_LRO; int lro = efx->net_dev->features & NETIF_F_LRO;
/* If we're in loopback test, then pass the packet directly to the
* loopback layer, and free the rx_buf here
*/
if (unlikely(efx->loopback_selftest)) {
efx_loopback_rx_packet(efx, rx_buf->data, rx_buf->len);
efx_free_rx_buffer(efx, rx_buf);
goto done;
}
if (rx_buf->skb) { if (rx_buf->skb) {
prefetch(skb_shinfo(rx_buf->skb)); prefetch(skb_shinfo(rx_buf->skb));
@ -736,7 +746,6 @@ void __efx_rx_packet(struct efx_channel *channel,
/* Update allocation strategy method */ /* Update allocation strategy method */
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
/* fall-thru */
done: done:
efx->net_dev->last_rx = jiffies; efx->net_dev->last_rx = jiffies;
} }

717
drivers/net/sfc/selftest.c Normal file
View File

@ -0,0 +1,717 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
* Copyright 2006-2008 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#include <linux/netdevice.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel_stat.h>
#include <linux/pci.h>
#include <linux/ethtool.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>
#include <linux/rtnetlink.h>
#include <asm/io.h>
#include "net_driver.h"
#include "ethtool.h"
#include "efx.h"
#include "falcon.h"
#include "selftest.h"
#include "boards.h"
#include "workarounds.h"
#include "mac.h"
/*
* Loopback test packet structure
*
* The self-test should stress every RSS vector, and unfortunately
* Falcon only performs RSS on TCP/UDP packets.
*/
struct efx_loopback_payload {
struct ethhdr header;
struct iphdr ip;
struct udphdr udp;
__be16 iteration;
const char msg[64];
} __attribute__ ((packed));
/* Loopback test source MAC address */
static const unsigned char payload_source[ETH_ALEN] = {
0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b,
};
static const char *payload_msg =
"Hello world! This is an Efx loopback test in progress!";
/**
* efx_selftest_state - persistent state during a selftest
* @flush: Drop all packets in efx_loopback_rx_packet
* @packet_count: Number of packets being used in this test
* @skbs: An array of skbs transmitted
* @rx_good: RX good packet count
* @rx_bad: RX bad packet count
* @payload: Payload used in tests
*/
struct efx_selftest_state {
int flush;
int packet_count;
struct sk_buff **skbs;
atomic_t rx_good;
atomic_t rx_bad;
struct efx_loopback_payload payload;
};
/**************************************************************************
*
* Configurable values
*
**************************************************************************/
/* Level of loopback testing
*
* The maximum packet burst length is 16**(n-1), i.e.
*
* - Level 0 : no packets
* - Level 1 : 1 packet
* - Level 2 : 17 packets (1 * 1 packet, 1 * 16 packets)
* - Level 3 : 273 packets (1 * 1 packet, 1 * 16 packet, 1 * 256 packets)
*
*/
static unsigned int loopback_test_level = 3;
/**************************************************************************
*
* Interrupt and event queue testing
*
**************************************************************************/
/* Test generation and receipt of interrupts */
static int efx_test_interrupts(struct efx_nic *efx,
struct efx_self_tests *tests)
{
struct efx_channel *channel;
EFX_LOG(efx, "testing interrupts\n");
tests->interrupt = -1;
/* Reset interrupt flag */
efx->last_irq_cpu = -1;
smp_wmb();
/* ACK each interrupting event queue. Receiving an interrupt due to
* traffic before a test event is raised is considered a pass */
efx_for_each_channel_with_interrupt(channel, efx) {
if (channel->work_pending)
efx_process_channel_now(channel);
if (efx->last_irq_cpu >= 0)
goto success;
}
falcon_generate_interrupt(efx);
/* Wait for arrival of test interrupt. */
EFX_LOG(efx, "waiting for test interrupt\n");
schedule_timeout_uninterruptible(HZ / 10);
if (efx->last_irq_cpu >= 0)
goto success;
EFX_ERR(efx, "timed out waiting for interrupt\n");
return -ETIMEDOUT;
success:
EFX_LOG(efx, "test interrupt (mode %d) seen on CPU%d\n",
efx->interrupt_mode, efx->last_irq_cpu);
tests->interrupt = 1;
return 0;
}
/* Test generation and receipt of non-interrupting events */
static int efx_test_eventq(struct efx_channel *channel,
struct efx_self_tests *tests)
{
unsigned int magic;
/* Channel specific code, limited to 20 bits */
magic = (0x00010150 + channel->channel);
EFX_LOG(channel->efx, "channel %d testing event queue with code %x\n",
channel->channel, magic);
tests->eventq_dma[channel->channel] = -1;
tests->eventq_int[channel->channel] = 1; /* fake pass */
tests->eventq_poll[channel->channel] = 1; /* fake pass */
/* Reset flag and zero magic word */
channel->efx->last_irq_cpu = -1;
channel->eventq_magic = 0;
smp_wmb();
falcon_generate_test_event(channel, magic);
udelay(1);
efx_process_channel_now(channel);
if (channel->eventq_magic != magic) {
EFX_ERR(channel->efx, "channel %d failed to see test event\n",
channel->channel);
return -ETIMEDOUT;
} else {
tests->eventq_dma[channel->channel] = 1;
}
return 0;
}
/* Test generation and receipt of interrupting events */
static int efx_test_eventq_irq(struct efx_channel *channel,
struct efx_self_tests *tests)
{
unsigned int magic, count;
/* Channel specific code, limited to 20 bits */
magic = (0x00010150 + channel->channel);
EFX_LOG(channel->efx, "channel %d testing event queue with code %x\n",
channel->channel, magic);
tests->eventq_dma[channel->channel] = -1;
tests->eventq_int[channel->channel] = -1;
tests->eventq_poll[channel->channel] = -1;
/* Reset flag and zero magic word */
channel->efx->last_irq_cpu = -1;
channel->eventq_magic = 0;
smp_wmb();
falcon_generate_test_event(channel, magic);
/* Wait for arrival of interrupt */
count = 0;
do {
schedule_timeout_uninterruptible(HZ / 100);
if (channel->work_pending)
efx_process_channel_now(channel);
if (channel->eventq_magic == magic)
goto eventq_ok;
} while (++count < 2);
EFX_ERR(channel->efx, "channel %d timed out waiting for event queue\n",
channel->channel);
/* See if interrupt arrived */
if (channel->efx->last_irq_cpu >= 0) {
EFX_ERR(channel->efx, "channel %d saw interrupt on CPU%d "
"during event queue test\n", channel->channel,
raw_smp_processor_id());
tests->eventq_int[channel->channel] = 1;
}
/* Check to see if event was received even if interrupt wasn't */
efx_process_channel_now(channel);
if (channel->eventq_magic == magic) {
EFX_ERR(channel->efx, "channel %d event was generated, but "
"failed to trigger an interrupt\n", channel->channel);
tests->eventq_dma[channel->channel] = 1;
}
return -ETIMEDOUT;
eventq_ok:
EFX_LOG(channel->efx, "channel %d event queue passed\n",
channel->channel);
tests->eventq_dma[channel->channel] = 1;
tests->eventq_int[channel->channel] = 1;
tests->eventq_poll[channel->channel] = 1;
return 0;
}
/**************************************************************************
*
* PHY testing
*
**************************************************************************/
/* Check PHY presence by reading the PHY ID registers */
static int efx_test_phy(struct efx_nic *efx,
struct efx_self_tests *tests)
{
u16 physid1, physid2;
struct mii_if_info *mii = &efx->mii;
struct net_device *net_dev = efx->net_dev;
if (efx->phy_type == PHY_TYPE_NONE)
return 0;
EFX_LOG(efx, "testing PHY presence\n");
tests->phy_ok = -1;
physid1 = mii->mdio_read(net_dev, mii->phy_id, MII_PHYSID1);
physid2 = mii->mdio_read(net_dev, mii->phy_id, MII_PHYSID2);
if ((physid1 != 0x0000) && (physid1 != 0xffff) &&
(physid2 != 0x0000) && (physid2 != 0xffff)) {
EFX_LOG(efx, "found MII PHY %d ID 0x%x:%x\n",
mii->phy_id, physid1, physid2);
tests->phy_ok = 1;
return 0;
}
EFX_ERR(efx, "no MII PHY present with ID %d\n", mii->phy_id);
return -ENODEV;
}
/**************************************************************************
*
* Loopback testing
* NB Only one loopback test can be executing concurrently.
*
**************************************************************************/
/* Loopback test RX callback
* This is called for each received packet during loopback testing.
*/
void efx_loopback_rx_packet(struct efx_nic *efx,
const char *buf_ptr, int pkt_len)
{
struct efx_selftest_state *state = efx->loopback_selftest;
struct efx_loopback_payload *received;
struct efx_loopback_payload *payload;
BUG_ON(!buf_ptr);
/* If we are just flushing, then drop the packet */
if ((state == NULL) || state->flush)
return;
payload = &state->payload;
received = (struct efx_loopback_payload *)(char *) buf_ptr;
received->ip.saddr = payload->ip.saddr;
received->ip.check = payload->ip.check;
/* Check that header exists */
if (pkt_len < sizeof(received->header)) {
EFX_ERR(efx, "saw runt RX packet (length %d) in %s loopback "
"test\n", pkt_len, LOOPBACK_MODE(efx));
goto err;
}
/* Check that the ethernet header exists */
if (memcmp(&received->header, &payload->header, ETH_HLEN) != 0) {
EFX_ERR(efx, "saw non-loopback RX packet in %s loopback test\n",
LOOPBACK_MODE(efx));
goto err;
}
/* Check packet length */
if (pkt_len != sizeof(*payload)) {
EFX_ERR(efx, "saw incorrect RX packet length %d (wanted %d) in "
"%s loopback test\n", pkt_len, (int)sizeof(*payload),
LOOPBACK_MODE(efx));
goto err;
}
/* Check that IP header matches */
if (memcmp(&received->ip, &payload->ip, sizeof(payload->ip)) != 0) {
EFX_ERR(efx, "saw corrupted IP header in %s loopback test\n",
LOOPBACK_MODE(efx));
goto err;
}
/* Check that msg and padding matches */
if (memcmp(&received->msg, &payload->msg, sizeof(received->msg)) != 0) {
EFX_ERR(efx, "saw corrupted RX packet in %s loopback test\n",
LOOPBACK_MODE(efx));
goto err;
}
/* Check that iteration matches */
if (received->iteration != payload->iteration) {
EFX_ERR(efx, "saw RX packet from iteration %d (wanted %d) in "
"%s loopback test\n", ntohs(received->iteration),
ntohs(payload->iteration), LOOPBACK_MODE(efx));
goto err;
}
/* Increase correct RX count */
EFX_TRACE(efx, "got loopback RX in %s loopback test\n",
LOOPBACK_MODE(efx));
atomic_inc(&state->rx_good);
return;
err:
#ifdef EFX_ENABLE_DEBUG
if (atomic_read(&state->rx_bad) == 0) {
EFX_ERR(efx, "received packet:\n");
print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1,
buf_ptr, pkt_len, 0);
EFX_ERR(efx, "expected packet:\n");
print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1,
&state->payload, sizeof(state->payload), 0);
}
#endif
atomic_inc(&state->rx_bad);
}
/* Initialise an efx_selftest_state for a new iteration */
static void efx_iterate_state(struct efx_nic *efx)
{
struct efx_selftest_state *state = efx->loopback_selftest;
struct net_device *net_dev = efx->net_dev;
struct efx_loopback_payload *payload = &state->payload;
/* Initialise the layerII header */
memcpy(&payload->header.h_dest, net_dev->dev_addr, ETH_ALEN);
memcpy(&payload->header.h_source, &payload_source, ETH_ALEN);
payload->header.h_proto = htons(ETH_P_IP);
/* saddr set later and used as incrementing count */
payload->ip.daddr = htonl(INADDR_LOOPBACK);
payload->ip.ihl = 5;
payload->ip.check = htons(0xdead);
payload->ip.tot_len = htons(sizeof(*payload) - sizeof(struct ethhdr));
payload->ip.version = IPVERSION;
payload->ip.protocol = IPPROTO_UDP;
/* Initialise udp header */
payload->udp.source = 0;
payload->udp.len = htons(sizeof(*payload) - sizeof(struct ethhdr) -
sizeof(struct iphdr));
payload->udp.check = 0; /* checksum ignored */
/* Fill out payload */
payload->iteration = htons(ntohs(payload->iteration) + 1);
memcpy(&payload->msg, payload_msg, sizeof(payload_msg));
/* Fill out remaining state members */
atomic_set(&state->rx_good, 0);
atomic_set(&state->rx_bad, 0);
smp_wmb();
}
static int efx_tx_loopback(struct efx_tx_queue *tx_queue)
{
struct efx_nic *efx = tx_queue->efx;
struct efx_selftest_state *state = efx->loopback_selftest;
struct efx_loopback_payload *payload;
struct sk_buff *skb;
int i, rc;
/* Transmit N copies of buffer */
for (i = 0; i < state->packet_count; i++) {
/* Allocate an skb, holding an extra reference for
* transmit completion counting */
skb = alloc_skb(sizeof(state->payload), GFP_KERNEL);
if (!skb)
return -ENOMEM;
state->skbs[i] = skb;
skb_get(skb);
/* Copy the payload in, incrementing the source address to
* exercise the rss vectors */
payload = ((struct efx_loopback_payload *)
skb_put(skb, sizeof(state->payload)));
memcpy(payload, &state->payload, sizeof(state->payload));
payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2));
/* Ensure everything we've written is visible to the
* interrupt handler. */
smp_wmb();
if (NET_DEV_REGISTERED(efx))
netif_tx_lock_bh(efx->net_dev);
rc = efx_xmit(efx, tx_queue, skb);
if (NET_DEV_REGISTERED(efx))
netif_tx_unlock_bh(efx->net_dev);
if (rc != NETDEV_TX_OK) {
EFX_ERR(efx, "TX queue %d could not transmit packet %d "
"of %d in %s loopback test\n", tx_queue->queue,
i + 1, state->packet_count, LOOPBACK_MODE(efx));
/* Defer cleaning up the other skbs for the caller */
kfree_skb(skb);
return -EPIPE;
}
}
return 0;
}
static int efx_rx_loopback(struct efx_tx_queue *tx_queue,
struct efx_loopback_self_tests *lb_tests)
{
struct efx_nic *efx = tx_queue->efx;
struct efx_selftest_state *state = efx->loopback_selftest;
struct sk_buff *skb;
int tx_done = 0, rx_good, rx_bad;
int i, rc = 0;
if (NET_DEV_REGISTERED(efx))
netif_tx_lock_bh(efx->net_dev);
/* Count the number of tx completions, and decrement the refcnt. Any
* skbs not already completed will be free'd when the queue is flushed */
for (i=0; i < state->packet_count; i++) {
skb = state->skbs[i];
if (skb && !skb_shared(skb))
++tx_done;
dev_kfree_skb_any(skb);
}
if (NET_DEV_REGISTERED(efx))
netif_tx_unlock_bh(efx->net_dev);
/* Check TX completion and received packet counts */
rx_good = atomic_read(&state->rx_good);
rx_bad = atomic_read(&state->rx_bad);
if (tx_done != state->packet_count) {
/* Don't free the skbs; they will be picked up on TX
* overflow or channel teardown.
*/
EFX_ERR(efx, "TX queue %d saw only %d out of an expected %d "
"TX completion events in %s loopback test\n",
tx_queue->queue, tx_done, state->packet_count,
LOOPBACK_MODE(efx));
rc = -ETIMEDOUT;
/* Allow to fall through so we see the RX errors as well */
}
/* We may always be up to a flush away from our desired packet total */
if (rx_good != state->packet_count) {
EFX_LOG(efx, "TX queue %d saw only %d out of an expected %d "
"received packets in %s loopback test\n",
tx_queue->queue, rx_good, state->packet_count,
LOOPBACK_MODE(efx));
rc = -ETIMEDOUT;
/* Fall through */
}
/* Update loopback test structure */
lb_tests->tx_sent[tx_queue->queue] += state->packet_count;
lb_tests->tx_done[tx_queue->queue] += tx_done;
lb_tests->rx_good += rx_good;
lb_tests->rx_bad += rx_bad;
return rc;
}
static int
efx_test_loopback(struct efx_tx_queue *tx_queue,
struct efx_loopback_self_tests *lb_tests)
{
struct efx_nic *efx = tx_queue->efx;
struct efx_selftest_state *state = efx->loopback_selftest;
struct efx_channel *channel;
int i, rc = 0;
for (i = 0; i < loopback_test_level; i++) {
/* Determine how many packets to send */
state->packet_count = (efx->type->txd_ring_mask + 1) / 3;
state->packet_count = min(1 << (i << 2), state->packet_count);
state->skbs = kzalloc(sizeof(state->skbs[0]) *
state->packet_count, GFP_KERNEL);
state->flush = 0;
EFX_LOG(efx, "TX queue %d testing %s loopback with %d "
"packets\n", tx_queue->queue, LOOPBACK_MODE(efx),
state->packet_count);
efx_iterate_state(efx);
rc = efx_tx_loopback(tx_queue);
/* NAPI polling is not enabled, so process channels synchronously */
schedule_timeout_uninterruptible(HZ / 50);
efx_for_each_channel_with_interrupt(channel, efx) {
if (channel->work_pending)
efx_process_channel_now(channel);
}
rc |= efx_rx_loopback(tx_queue, lb_tests);
kfree(state->skbs);
if (rc) {
/* Wait a while to ensure there are no packets
* floating around after a failure. */
schedule_timeout_uninterruptible(HZ / 10);
return rc;
}
}
EFX_LOG(efx, "TX queue %d passed %s loopback test with a burst length "
"of %d packets\n", tx_queue->queue, LOOPBACK_MODE(efx),
state->packet_count);
return rc;
}
static int efx_test_loopbacks(struct efx_nic *efx,
struct efx_self_tests *tests,
unsigned int loopback_modes)
{
struct efx_selftest_state *state = efx->loopback_selftest;
struct ethtool_cmd ecmd, ecmd_loopback;
struct efx_tx_queue *tx_queue;
enum efx_loopback_mode old_mode, mode;
int count, rc = 0, link_up;
rc = efx_ethtool_get_settings(efx->net_dev, &ecmd);
if (rc) {
EFX_ERR(efx, "could not get GMII settings\n");
return rc;
}
old_mode = efx->loopback_mode;
/* Disable autonegotiation for the purposes of loopback */
memcpy(&ecmd_loopback, &ecmd, sizeof(ecmd_loopback));
if (ecmd_loopback.autoneg == AUTONEG_ENABLE) {
ecmd_loopback.autoneg = AUTONEG_DISABLE;
ecmd_loopback.duplex = DUPLEX_FULL;
ecmd_loopback.speed = SPEED_10000;
}
rc = efx_ethtool_set_settings(efx->net_dev, &ecmd_loopback);
if (rc) {
EFX_ERR(efx, "could not disable autonegotiation\n");
goto out;
}
tests->loopback_speed = ecmd_loopback.speed;
tests->loopback_full_duplex = ecmd_loopback.duplex;
/* Test all supported loopback modes */
for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) {
if (!(loopback_modes & (1 << mode)))
continue;
/* Move the port into the specified loopback mode. */
state->flush = 1;
efx->loopback_mode = mode;
efx_reconfigure_port(efx);
/* Wait for the PHY to signal the link is up */
count = 0;
do {
struct efx_channel *channel = &efx->channel[0];
falcon_check_xmac(efx);
schedule_timeout_uninterruptible(HZ / 10);
if (channel->work_pending)
efx_process_channel_now(channel);
/* Wait for PHY events to be processed */
flush_workqueue(efx->workqueue);
rmb();
/* efx->link_up can be 1 even if the XAUI link is down,
* (bug5762). Usually, it's not worth bothering with the
* difference, but for selftests, we need that extra
* guarantee that the link is really, really, up.
*/
link_up = efx->link_up;
if (!falcon_xaui_link_ok(efx))
link_up = 0;
} while ((++count < 20) && !link_up);
/* The link should now be up. If it isn't, there is no point
* in attempting a loopback test */
if (!link_up) {
EFX_ERR(efx, "loopback %s never came up\n",
LOOPBACK_MODE(efx));
rc = -EIO;
goto out;
}
EFX_LOG(efx, "link came up in %s loopback in %d iterations\n",
LOOPBACK_MODE(efx), count);
/* Test every TX queue */
efx_for_each_tx_queue(tx_queue, efx) {
rc |= efx_test_loopback(tx_queue,
&tests->loopback[mode]);
if (rc)
goto out;
}
}
out:
/* Take out of loopback and restore PHY settings */
state->flush = 1;
efx->loopback_mode = old_mode;
efx_ethtool_set_settings(efx->net_dev, &ecmd);
return rc;
}
/**************************************************************************
*
* Entry points
*
*************************************************************************/
/* Online (i.e. non-disruptive) testing
* This checks interrupt generation, event delivery and PHY presence. */
int efx_online_test(struct efx_nic *efx, struct efx_self_tests *tests)
{
struct efx_channel *channel;
int rc = 0;
EFX_LOG(efx, "performing online self-tests\n");
rc |= efx_test_interrupts(efx, tests);
efx_for_each_channel(channel, efx) {
if (channel->has_interrupt)
rc |= efx_test_eventq_irq(channel, tests);
else
rc |= efx_test_eventq(channel, tests);
}
rc |= efx_test_phy(efx, tests);
if (rc)
EFX_ERR(efx, "failed online self-tests\n");
return rc;
}
/* Offline (i.e. disruptive) testing
* This checks MAC and PHY loopback on the specified port. */
int efx_offline_test(struct efx_nic *efx,
struct efx_self_tests *tests, unsigned int loopback_modes)
{
struct efx_selftest_state *state;
int rc = 0;
EFX_LOG(efx, "performing offline self-tests\n");
/* Create a selftest_state structure to hold state for the test */
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL) {
rc = -ENOMEM;
goto out;
}
/* Set the port loopback_selftest member. From this point on
* all received packets will be dropped. Mark the state as
* "flushing" so all inflight packets are dropped */
BUG_ON(efx->loopback_selftest);
state->flush = 1;
efx->loopback_selftest = (void *)state;
rc = efx_test_loopbacks(efx, tests, loopback_modes);
efx->loopback_selftest = NULL;
wmb();
kfree(state);
out:
if (rc)
EFX_ERR(efx, "failed offline self-tests\n");
return rc;
}

View File

@ -0,0 +1,50 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
* Copyright 2006-2008 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#ifndef EFX_SELFTEST_H
#define EFX_SELFTEST_H
#include "net_driver.h"
/*
* Self tests
*/
struct efx_loopback_self_tests {
int tx_sent[EFX_MAX_TX_QUEUES];
int tx_done[EFX_MAX_TX_QUEUES];
int rx_good;
int rx_bad;
};
/* Efx self test results
* For fields which are not counters, 1 indicates success and -1
* indicates failure.
*/
struct efx_self_tests {
int interrupt;
int eventq_dma[EFX_MAX_CHANNELS];
int eventq_int[EFX_MAX_CHANNELS];
int eventq_poll[EFX_MAX_CHANNELS];
int phy_ok;
int loopback_speed;
int loopback_full_duplex;
struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX];
};
extern void efx_loopback_rx_packet(struct efx_nic *efx,
const char *buf_ptr, int pkt_len);
extern int efx_online_test(struct efx_nic *efx,
struct efx_self_tests *tests);
extern int efx_offline_test(struct efx_nic *efx,
struct efx_self_tests *tests,
unsigned int loopback_modes);
#endif /* EFX_SELFTEST_H */

View File

@ -130,6 +130,15 @@ void sfe4001_poweroff(struct efx_nic *efx)
(void) efx_i2c_read(i2c, MAX6647, RSL, &in, 1); (void) efx_i2c_read(i2c, MAX6647, RSL, &in, 1);
} }
/* The P0_EN_3V3X line on SFE4001 boards (from A2 onward) is connected
* to the FLASH_CFG_1 input on the DSP. We must keep it high at power-
* up to allow writing the flash (done through MDIO from userland).
*/
unsigned int sfe4001_phy_flash_cfg;
module_param_named(phy_flash_cfg, sfe4001_phy_flash_cfg, uint, 0444);
MODULE_PARM_DESC(phy_flash_cfg,
"Force PHY to enter flash configuration mode");
/* This board uses an I2C expander to provider power to the PHY, which needs to /* This board uses an I2C expander to provider power to the PHY, which needs to
* be turned on before the PHY can be used. * be turned on before the PHY can be used.
* Context: Process context, rtnl lock held * Context: Process context, rtnl lock held
@ -203,6 +212,8 @@ int sfe4001_poweron(struct efx_nic *efx)
out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) |
(1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) |
(1 << P0_X_TRST_LBN)); (1 << P0_X_TRST_LBN));
if (sfe4001_phy_flash_cfg)
out |= 1 << P0_EN_3V3X_LBN;
rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1);
if (rc) if (rc)
@ -226,6 +237,9 @@ int sfe4001_poweron(struct efx_nic *efx)
if (in & (1 << P1_AFE_PWD_LBN)) if (in & (1 << P1_AFE_PWD_LBN))
goto done; goto done;
/* DSP doesn't look powered in flash config mode */
if (sfe4001_phy_flash_cfg)
goto done;
} while (++count < 20); } while (++count < 20);
EFX_INFO(efx, "timed out waiting for power\n"); EFX_INFO(efx, "timed out waiting for power\n");

View File

@ -24,6 +24,11 @@
MDIO_MMDREG_DEVS0_PCS | \ MDIO_MMDREG_DEVS0_PCS | \
MDIO_MMDREG_DEVS0_PHYXS) MDIO_MMDREG_DEVS0_PHYXS)
#define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \
(1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
(1 << LOOPBACK_NETWORK))
/* We complain if we fail to see the link partner as 10G capable this many /* We complain if we fail to see the link partner as 10G capable this many
* times in a row (must be > 1 as sampling the autoneg. registers is racy) * times in a row (must be > 1 as sampling the autoneg. registers is racy)
*/ */
@ -72,6 +77,10 @@
#define PMA_PMD_BIST_RXD_LBN (1) #define PMA_PMD_BIST_RXD_LBN (1)
#define PMA_PMD_BIST_AFE_LBN (0) #define PMA_PMD_BIST_AFE_LBN (0)
/* Special Software reset register */
#define PMA_PMD_EXT_CTRL_REG 49152
#define PMA_PMD_EXT_SSR_LBN 15
#define BIST_MAX_DELAY (1000) #define BIST_MAX_DELAY (1000)
#define BIST_POLL_DELAY (10) #define BIST_POLL_DELAY (10)
@ -86,6 +95,11 @@
#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */ #define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */
#define CLK312_EN_LBN 3 #define CLK312_EN_LBN 3
/* PHYXS registers */
#define PHYXS_TEST1 (49162)
#define LOOPBACK_NEAR_LBN (8)
#define LOOPBACK_NEAR_WIDTH (1)
/* Boot status register */ /* Boot status register */
#define PCS_BOOT_STATUS_REG (0xd000) #define PCS_BOOT_STATUS_REG (0xd000)
#define PCS_BOOT_FATAL_ERR_LBN (0) #define PCS_BOOT_FATAL_ERR_LBN (0)
@ -106,7 +120,9 @@ MODULE_PARM_DESC(crc_error_reset_threshold,
struct tenxpress_phy_data { struct tenxpress_phy_data {
enum tenxpress_state state; enum tenxpress_state state;
enum efx_loopback_mode loopback_mode;
atomic_t bad_crc_count; atomic_t bad_crc_count;
int tx_disabled;
int bad_lp_tries; int bad_lp_tries;
}; };
@ -199,10 +215,12 @@ static int tenxpress_phy_init(struct efx_nic *efx)
tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL); tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL);
rc = mdio_clause45_wait_reset_mmds(efx, if (!sfe4001_phy_flash_cfg) {
TENXPRESS_REQUIRED_DEVS); rc = mdio_clause45_wait_reset_mmds(efx,
if (rc < 0) TENXPRESS_REQUIRED_DEVS);
goto fail; if (rc < 0)
goto fail;
}
rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
if (rc < 0) if (rc < 0)
@ -225,6 +243,35 @@ static int tenxpress_phy_init(struct efx_nic *efx)
return rc; return rc;
} }
static int tenxpress_special_reset(struct efx_nic *efx)
{
int rc, reg;
EFX_TRACE(efx, "%s\n", __func__);
/* Initiate reset */
reg = mdio_clause45_read(efx, efx->mii.phy_id,
MDIO_MMD_PMAPMD, PMA_PMD_EXT_CTRL_REG);
reg |= (1 << PMA_PMD_EXT_SSR_LBN);
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
PMA_PMD_EXT_CTRL_REG, reg);
msleep(200);
/* Wait for the blocks to come out of reset */
rc = mdio_clause45_wait_reset_mmds(efx,
TENXPRESS_REQUIRED_DEVS);
if (rc < 0)
return rc;
/* Try and reconfigure the device */
rc = tenxpress_init(efx);
if (rc < 0)
return rc;
return 0;
}
static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp) static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp)
{ {
struct tenxpress_phy_data *pd = efx->phy_data; struct tenxpress_phy_data *pd = efx->phy_data;
@ -299,11 +346,46 @@ static int tenxpress_link_ok(struct efx_nic *efx, int check_lp)
return ok; return ok;
} }
static void tenxpress_phyxs_loopback(struct efx_nic *efx)
{
int phy_id = efx->mii.phy_id;
int ctrl1, ctrl2;
ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
PHYXS_TEST1);
if (efx->loopback_mode == LOOPBACK_PHYXS)
ctrl2 |= (1 << LOOPBACK_NEAR_LBN);
else
ctrl2 &= ~(1 << LOOPBACK_NEAR_LBN);
if (ctrl1 != ctrl2)
mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS,
PHYXS_TEST1, ctrl2);
}
static void tenxpress_phy_reconfigure(struct efx_nic *efx) static void tenxpress_phy_reconfigure(struct efx_nic *efx)
{ {
struct tenxpress_phy_data *phy_data = efx->phy_data;
int loop_change = LOOPBACK_OUT_OF(phy_data, efx,
TENXPRESS_LOOPBACKS);
if (!tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL)) if (!tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL))
return; return;
/* When coming out of transmit disable, coming out of low power
* mode, or moving out of any PHY internal loopback mode,
* perform a special software reset */
if ((phy_data->tx_disabled && !efx->tx_disabled) ||
loop_change) {
(void) tenxpress_special_reset(efx);
falcon_reset_xaui(efx);
}
mdio_clause45_transmit_disable(efx);
mdio_clause45_phy_reconfigure(efx);
tenxpress_phyxs_loopback(efx);
phy_data->tx_disabled = efx->tx_disabled;
phy_data->loopback_mode = efx->loopback_mode;
efx->link_up = tenxpress_link_ok(efx, 0); efx->link_up = tenxpress_link_ok(efx, 0);
efx->link_options = GM_LPA_10000FULL; efx->link_options = GM_LPA_10000FULL;
} }
@ -431,4 +513,5 @@ struct efx_phy_operations falcon_tenxpress_phy_ops = {
.clear_interrupt = tenxpress_phy_clear_interrupt, .clear_interrupt = tenxpress_phy_clear_interrupt,
.reset_xaui = tenxpress_reset_xaui, .reset_xaui = tenxpress_reset_xaui,
.mmds = TENXPRESS_REQUIRED_DEVS, .mmds = TENXPRESS_REQUIRED_DEVS,
.loopbacks = TENXPRESS_LOOPBACKS,
}; };

View File

@ -82,6 +82,46 @@ static inline void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
} }
} }
/**
* struct efx_tso_header - a DMA mapped buffer for packet headers
* @next: Linked list of free ones.
* The list is protected by the TX queue lock.
* @dma_unmap_len: Length to unmap for an oversize buffer, or 0.
* @dma_addr: The DMA address of the header below.
*
* This controls the memory used for a TSO header. Use TSOH_DATA()
* to find the packet header data. Use TSOH_SIZE() to calculate the
* total size required for a given packet header length. TSO headers
* in the free list are exactly %TSOH_STD_SIZE bytes in size.
*/
struct efx_tso_header {
union {
struct efx_tso_header *next;
size_t unmap_len;
};
dma_addr_t dma_addr;
};
static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
const struct sk_buff *skb);
static void efx_fini_tso(struct efx_tx_queue *tx_queue);
static void efx_tsoh_heap_free(struct efx_tx_queue *tx_queue,
struct efx_tso_header *tsoh);
static inline void efx_tsoh_free(struct efx_tx_queue *tx_queue,
struct efx_tx_buffer *buffer)
{
if (buffer->tsoh) {
if (likely(!buffer->tsoh->unmap_len)) {
buffer->tsoh->next = tx_queue->tso_headers_free;
tx_queue->tso_headers_free = buffer->tsoh;
} else {
efx_tsoh_heap_free(tx_queue, buffer->tsoh);
}
buffer->tsoh = NULL;
}
}
/* /*
* Add a socket buffer to a TX queue * Add a socket buffer to a TX queue
@ -114,6 +154,9 @@ static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue,
EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
if (skb_shinfo((struct sk_buff *)skb)->gso_size)
return efx_enqueue_skb_tso(tx_queue, skb);
/* Get size of the initial fragment */ /* Get size of the initial fragment */
len = skb_headlen(skb); len = skb_headlen(skb);
@ -166,6 +209,8 @@ static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue,
insert_ptr = (tx_queue->insert_count & insert_ptr = (tx_queue->insert_count &
efx->type->txd_ring_mask); efx->type->txd_ring_mask);
buffer = &tx_queue->buffer[insert_ptr]; buffer = &tx_queue->buffer[insert_ptr];
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->tsoh);
EFX_BUG_ON_PARANOID(buffer->skb); EFX_BUG_ON_PARANOID(buffer->skb);
EFX_BUG_ON_PARANOID(buffer->len); EFX_BUG_ON_PARANOID(buffer->len);
EFX_BUG_ON_PARANOID(buffer->continuation != 1); EFX_BUG_ON_PARANOID(buffer->continuation != 1);
@ -432,6 +477,9 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
efx_release_tx_buffers(tx_queue); efx_release_tx_buffers(tx_queue);
/* Free up TSO header cache */
efx_fini_tso(tx_queue);
/* Release queue's stop on port, if any */ /* Release queue's stop on port, if any */
if (tx_queue->stopped) { if (tx_queue->stopped) {
tx_queue->stopped = 0; tx_queue->stopped = 0;
@ -450,3 +498,619 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
} }
/* Efx TCP segmentation acceleration.
*
* Why? Because by doing it here in the driver we can go significantly
* faster than the GSO.
*
* Requires TX checksum offload support.
*/
/* Number of bytes inserted at the start of a TSO header buffer,
* similar to NET_IP_ALIGN.
*/
#if defined(__i386__) || defined(__x86_64__)
#define TSOH_OFFSET 0
#else
#define TSOH_OFFSET NET_IP_ALIGN
#endif
#define TSOH_BUFFER(tsoh) ((u8 *)(tsoh + 1) + TSOH_OFFSET)
/* Total size of struct efx_tso_header, buffer and padding */
#define TSOH_SIZE(hdr_len) \
(sizeof(struct efx_tso_header) + TSOH_OFFSET + hdr_len)
/* Size of blocks on free list. Larger blocks must be allocated from
* the heap.
*/
#define TSOH_STD_SIZE 128
#define PTR_DIFF(p1, p2) ((u8 *)(p1) - (u8 *)(p2))
#define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data)
#define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data)
#define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data)
/**
* struct tso_state - TSO state for an SKB
* @remaining_len: Bytes of data we've yet to segment
* @seqnum: Current sequence number
* @packet_space: Remaining space in current packet
* @ifc: Input fragment cursor.
* Where we are in the current fragment of the incoming SKB. These
* values get updated in place when we split a fragment over
* multiple packets.
* @p: Parameters.
* These values are set once at the start of the TSO send and do
* not get changed as the routine progresses.
*
* The state used during segmentation. It is put into this data structure
* just to make it easy to pass into inline functions.
*/
struct tso_state {
unsigned remaining_len;
unsigned seqnum;
unsigned packet_space;
struct {
/* DMA address of current position */
dma_addr_t dma_addr;
/* Remaining length */
unsigned int len;
/* DMA address and length of the whole fragment */
unsigned int unmap_len;
dma_addr_t unmap_addr;
struct page *page;
unsigned page_off;
} ifc;
struct {
/* The number of bytes of header */
unsigned int header_length;
/* The number of bytes to put in each outgoing segment. */
int full_packet_size;
/* Current IPv4 ID, host endian. */
unsigned ipv4_id;
} p;
};
/*
* Verify that our various assumptions about sk_buffs and the conditions
* under which TSO will be attempted hold true.
*/
static inline void efx_tso_check_safe(const struct sk_buff *skb)
{
EFX_BUG_ON_PARANOID(skb->protocol != htons(ETH_P_IP));
EFX_BUG_ON_PARANOID(((struct ethhdr *)skb->data)->h_proto !=
skb->protocol);
EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP);
EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data)
+ (tcp_hdr(skb)->doff << 2u)) >
skb_headlen(skb));
}
/*
* Allocate a page worth of efx_tso_header structures, and string them
* into the tx_queue->tso_headers_free linked list. Return 0 or -ENOMEM.
*/
static int efx_tsoh_block_alloc(struct efx_tx_queue *tx_queue)
{
struct pci_dev *pci_dev = tx_queue->efx->pci_dev;
struct efx_tso_header *tsoh;
dma_addr_t dma_addr;
u8 *base_kva, *kva;
base_kva = pci_alloc_consistent(pci_dev, PAGE_SIZE, &dma_addr);
if (base_kva == NULL) {
EFX_ERR(tx_queue->efx, "Unable to allocate page for TSO"
" headers\n");
return -ENOMEM;
}
/* pci_alloc_consistent() allocates pages. */
EFX_BUG_ON_PARANOID(dma_addr & (PAGE_SIZE - 1u));
for (kva = base_kva; kva < base_kva + PAGE_SIZE; kva += TSOH_STD_SIZE) {
tsoh = (struct efx_tso_header *)kva;
tsoh->dma_addr = dma_addr + (TSOH_BUFFER(tsoh) - base_kva);
tsoh->next = tx_queue->tso_headers_free;
tx_queue->tso_headers_free = tsoh;
}
return 0;
}
/* Free up a TSO header, and all others in the same page. */
static void efx_tsoh_block_free(struct efx_tx_queue *tx_queue,
struct efx_tso_header *tsoh,
struct pci_dev *pci_dev)
{
struct efx_tso_header **p;
unsigned long base_kva;
dma_addr_t base_dma;
base_kva = (unsigned long)tsoh & PAGE_MASK;
base_dma = tsoh->dma_addr & PAGE_MASK;
p = &tx_queue->tso_headers_free;
while (*p != NULL)
if (((unsigned long)*p & PAGE_MASK) == base_kva)
*p = (*p)->next;
else
p = &(*p)->next;
pci_free_consistent(pci_dev, PAGE_SIZE, (void *)base_kva, base_dma);
}
static struct efx_tso_header *
efx_tsoh_heap_alloc(struct efx_tx_queue *tx_queue, size_t header_len)
{
struct efx_tso_header *tsoh;
tsoh = kmalloc(TSOH_SIZE(header_len), GFP_ATOMIC | GFP_DMA);
if (unlikely(!tsoh))
return NULL;
tsoh->dma_addr = pci_map_single(tx_queue->efx->pci_dev,
TSOH_BUFFER(tsoh), header_len,
PCI_DMA_TODEVICE);
if (unlikely(pci_dma_mapping_error(tsoh->dma_addr))) {
kfree(tsoh);
return NULL;
}
tsoh->unmap_len = header_len;
return tsoh;
}
static void
efx_tsoh_heap_free(struct efx_tx_queue *tx_queue, struct efx_tso_header *tsoh)
{
pci_unmap_single(tx_queue->efx->pci_dev,
tsoh->dma_addr, tsoh->unmap_len,
PCI_DMA_TODEVICE);
kfree(tsoh);
}
/**
* efx_tx_queue_insert - push descriptors onto the TX queue
* @tx_queue: Efx TX queue
* @dma_addr: DMA address of fragment
* @len: Length of fragment
* @skb: Only non-null for end of last segment
* @end_of_packet: True if last fragment in a packet
* @unmap_addr: DMA address of fragment for unmapping
* @unmap_len: Only set this in last segment of a fragment
*
* Push descriptors onto the TX queue. Return 0 on success or 1 if
* @tx_queue full.
*/
static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue,
dma_addr_t dma_addr, unsigned len,
const struct sk_buff *skb, int end_of_packet,
dma_addr_t unmap_addr, unsigned unmap_len)
{
struct efx_tx_buffer *buffer;
struct efx_nic *efx = tx_queue->efx;
unsigned dma_len, fill_level, insert_ptr, misalign;
int q_space;
EFX_BUG_ON_PARANOID(len <= 0);
fill_level = tx_queue->insert_count - tx_queue->old_read_count;
/* -1 as there is no way to represent all descriptors used */
q_space = efx->type->txd_ring_mask - 1 - fill_level;
while (1) {
if (unlikely(q_space-- <= 0)) {
/* It might be that completions have happened
* since the xmit path last checked. Update
* the xmit path's copy of read_count.
*/
++tx_queue->stopped;
/* This memory barrier protects the change of
* stopped from the access of read_count. */
smp_mb();
tx_queue->old_read_count =
*(volatile unsigned *)&tx_queue->read_count;
fill_level = (tx_queue->insert_count
- tx_queue->old_read_count);
q_space = efx->type->txd_ring_mask - 1 - fill_level;
if (unlikely(q_space-- <= 0))
return 1;
smp_mb();
--tx_queue->stopped;
}
insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask;
buffer = &tx_queue->buffer[insert_ptr];
++tx_queue->insert_count;
EFX_BUG_ON_PARANOID(tx_queue->insert_count -
tx_queue->read_count >
efx->type->txd_ring_mask);
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->len);
EFX_BUG_ON_PARANOID(buffer->unmap_len);
EFX_BUG_ON_PARANOID(buffer->skb);
EFX_BUG_ON_PARANOID(buffer->continuation != 1);
EFX_BUG_ON_PARANOID(buffer->tsoh);
buffer->dma_addr = dma_addr;
/* Ensure we do not cross a boundary unsupported by H/W */
dma_len = (~dma_addr & efx->type->tx_dma_mask) + 1;
misalign = (unsigned)dma_addr & efx->type->bug5391_mask;
if (misalign && dma_len + misalign > 512)
dma_len = 512 - misalign;
/* If there is enough space to send then do so */
if (dma_len >= len)
break;
buffer->len = dma_len; /* Don't set the other members */
dma_addr += dma_len;
len -= dma_len;
}
EFX_BUG_ON_PARANOID(!len);
buffer->len = len;
buffer->skb = skb;
buffer->continuation = !end_of_packet;
buffer->unmap_addr = unmap_addr;
buffer->unmap_len = unmap_len;
return 0;
}
/*
* Put a TSO header into the TX queue.
*
* This is special-cased because we know that it is small enough to fit in
* a single fragment, and we know it doesn't cross a page boundary. It
* also allows us to not worry about end-of-packet etc.
*/
static inline void efx_tso_put_header(struct efx_tx_queue *tx_queue,
struct efx_tso_header *tsoh, unsigned len)
{
struct efx_tx_buffer *buffer;
buffer = &tx_queue->buffer[tx_queue->insert_count &
tx_queue->efx->type->txd_ring_mask];
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->len);
EFX_BUG_ON_PARANOID(buffer->unmap_len);
EFX_BUG_ON_PARANOID(buffer->skb);
EFX_BUG_ON_PARANOID(buffer->continuation != 1);
EFX_BUG_ON_PARANOID(buffer->tsoh);
buffer->len = len;
buffer->dma_addr = tsoh->dma_addr;
buffer->tsoh = tsoh;
++tx_queue->insert_count;
}
/* Remove descriptors put into a tx_queue. */
static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue)
{
struct efx_tx_buffer *buffer;
/* Work backwards until we hit the original insert pointer value */
while (tx_queue->insert_count != tx_queue->write_count) {
--tx_queue->insert_count;
buffer = &tx_queue->buffer[tx_queue->insert_count &
tx_queue->efx->type->txd_ring_mask];
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->skb);
buffer->len = 0;
buffer->continuation = 1;
if (buffer->unmap_len) {
pci_unmap_page(tx_queue->efx->pci_dev,
buffer->unmap_addr,
buffer->unmap_len, PCI_DMA_TODEVICE);
buffer->unmap_len = 0;
}
}
}
/* Parse the SKB header and initialise state. */
static inline void tso_start(struct tso_state *st, const struct sk_buff *skb)
{
/* All ethernet/IP/TCP headers combined size is TCP header size
* plus offset of TCP header relative to start of packet.
*/
st->p.header_length = ((tcp_hdr(skb)->doff << 2u)
+ PTR_DIFF(tcp_hdr(skb), skb->data));
st->p.full_packet_size = (st->p.header_length
+ skb_shinfo(skb)->gso_size);
st->p.ipv4_id = ntohs(ip_hdr(skb)->id);
st->seqnum = ntohl(tcp_hdr(skb)->seq);
EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg);
EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn);
EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst);
st->packet_space = st->p.full_packet_size;
st->remaining_len = skb->len - st->p.header_length;
}
/**
* tso_get_fragment - record fragment details and map for DMA
* @st: TSO state
* @efx: Efx NIC
* @data: Pointer to fragment data
* @len: Length of fragment
*
* Record fragment details and map for DMA. Return 0 on success, or
* -%ENOMEM if DMA mapping fails.
*/
static inline int tso_get_fragment(struct tso_state *st, struct efx_nic *efx,
int len, struct page *page, int page_off)
{
st->ifc.unmap_addr = pci_map_page(efx->pci_dev, page, page_off,
len, PCI_DMA_TODEVICE);
if (likely(!pci_dma_mapping_error(st->ifc.unmap_addr))) {
st->ifc.unmap_len = len;
st->ifc.len = len;
st->ifc.dma_addr = st->ifc.unmap_addr;
st->ifc.page = page;
st->ifc.page_off = page_off;
return 0;
}
return -ENOMEM;
}
/**
* tso_fill_packet_with_fragment - form descriptors for the current fragment
* @tx_queue: Efx TX queue
* @skb: Socket buffer
* @st: TSO state
*
* Form descriptors for the current fragment, until we reach the end
* of fragment or end-of-packet. Return 0 on success, 1 if not enough
* space in @tx_queue.
*/
static inline int tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue,
const struct sk_buff *skb,
struct tso_state *st)
{
int n, end_of_packet, rc;
if (st->ifc.len == 0)
return 0;
if (st->packet_space == 0)
return 0;
EFX_BUG_ON_PARANOID(st->ifc.len <= 0);
EFX_BUG_ON_PARANOID(st->packet_space <= 0);
n = min(st->ifc.len, st->packet_space);
st->packet_space -= n;
st->remaining_len -= n;
st->ifc.len -= n;
st->ifc.page_off += n;
end_of_packet = st->remaining_len == 0 || st->packet_space == 0;
rc = efx_tx_queue_insert(tx_queue, st->ifc.dma_addr, n,
st->remaining_len ? NULL : skb,
end_of_packet, st->ifc.unmap_addr,
st->ifc.len ? 0 : st->ifc.unmap_len);
st->ifc.dma_addr += n;
return rc;
}
/**
* tso_start_new_packet - generate a new header and prepare for the new packet
* @tx_queue: Efx TX queue
* @skb: Socket buffer
* @st: TSO state
*
* Generate a new header and prepare for the new packet. Return 0 on
* success, or -1 if failed to alloc header.
*/
static inline int tso_start_new_packet(struct efx_tx_queue *tx_queue,
const struct sk_buff *skb,
struct tso_state *st)
{
struct efx_tso_header *tsoh;
struct iphdr *tsoh_iph;
struct tcphdr *tsoh_th;
unsigned ip_length;
u8 *header;
/* Allocate a DMA-mapped header buffer. */
if (likely(TSOH_SIZE(st->p.header_length) <= TSOH_STD_SIZE)) {
if (tx_queue->tso_headers_free == NULL)
if (efx_tsoh_block_alloc(tx_queue))
return -1;
EFX_BUG_ON_PARANOID(!tx_queue->tso_headers_free);
tsoh = tx_queue->tso_headers_free;
tx_queue->tso_headers_free = tsoh->next;
tsoh->unmap_len = 0;
} else {
tx_queue->tso_long_headers++;
tsoh = efx_tsoh_heap_alloc(tx_queue, st->p.header_length);
if (unlikely(!tsoh))
return -1;
}
header = TSOH_BUFFER(tsoh);
tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb));
tsoh_iph = (struct iphdr *)(header + SKB_IPV4_OFF(skb));
/* Copy and update the headers. */
memcpy(header, skb->data, st->p.header_length);
tsoh_th->seq = htonl(st->seqnum);
st->seqnum += skb_shinfo(skb)->gso_size;
if (st->remaining_len > skb_shinfo(skb)->gso_size) {
/* This packet will not finish the TSO burst. */
ip_length = st->p.full_packet_size - ETH_HDR_LEN(skb);
tsoh_th->fin = 0;
tsoh_th->psh = 0;
} else {
/* This packet will be the last in the TSO burst. */
ip_length = (st->p.header_length - ETH_HDR_LEN(skb)
+ st->remaining_len);
tsoh_th->fin = tcp_hdr(skb)->fin;
tsoh_th->psh = tcp_hdr(skb)->psh;
}
tsoh_iph->tot_len = htons(ip_length);
/* Linux leaves suitable gaps in the IP ID space for us to fill. */
tsoh_iph->id = htons(st->p.ipv4_id);
st->p.ipv4_id++;
st->packet_space = skb_shinfo(skb)->gso_size;
++tx_queue->tso_packets;
/* Form a descriptor for this header. */
efx_tso_put_header(tx_queue, tsoh, st->p.header_length);
return 0;
}
/**
* efx_enqueue_skb_tso - segment and transmit a TSO socket buffer
* @tx_queue: Efx TX queue
* @skb: Socket buffer
*
* Context: You must hold netif_tx_lock() to call this function.
*
* Add socket buffer @skb to @tx_queue, doing TSO or return != 0 if
* @skb was not enqueued. In all cases @skb is consumed. Return
* %NETDEV_TX_OK or %NETDEV_TX_BUSY.
*/
static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
const struct sk_buff *skb)
{
int frag_i, rc, rc2 = NETDEV_TX_OK;
struct tso_state state;
skb_frag_t *f;
/* Verify TSO is safe - these checks should never fail. */
efx_tso_check_safe(skb);
EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
tso_start(&state, skb);
/* Assume that skb header area contains exactly the headers, and
* all payload is in the frag list.
*/
if (skb_headlen(skb) == state.p.header_length) {
/* Grab the first payload fragment. */
EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1);
frag_i = 0;
f = &skb_shinfo(skb)->frags[frag_i];
rc = tso_get_fragment(&state, tx_queue->efx,
f->size, f->page, f->page_offset);
if (rc)
goto mem_err;
} else {
/* It may look like this code fragment assumes that the
* skb->data portion does not cross a page boundary, but
* that is not the case. It is guaranteed to be direct
* mapped memory, and therefore is physically contiguous,
* and so DMA will work fine. kmap_atomic() on this region
* will just return the direct mapping, so that will work
* too.
*/
int page_off = (unsigned long)skb->data & (PAGE_SIZE - 1);
int hl = state.p.header_length;
rc = tso_get_fragment(&state, tx_queue->efx,
skb_headlen(skb) - hl,
virt_to_page(skb->data), page_off + hl);
if (rc)
goto mem_err;
frag_i = -1;
}
if (tso_start_new_packet(tx_queue, skb, &state) < 0)
goto mem_err;
while (1) {
rc = tso_fill_packet_with_fragment(tx_queue, skb, &state);
if (unlikely(rc))
goto stop;
/* Move onto the next fragment? */
if (state.ifc.len == 0) {
if (++frag_i >= skb_shinfo(skb)->nr_frags)
/* End of payload reached. */
break;
f = &skb_shinfo(skb)->frags[frag_i];
rc = tso_get_fragment(&state, tx_queue->efx,
f->size, f->page, f->page_offset);
if (rc)
goto mem_err;
}
/* Start at new packet? */
if (state.packet_space == 0 &&
tso_start_new_packet(tx_queue, skb, &state) < 0)
goto mem_err;
}
/* Pass off to hardware */
falcon_push_buffers(tx_queue);
tx_queue->tso_bursts++;
return NETDEV_TX_OK;
mem_err:
EFX_ERR(tx_queue->efx, "Out of memory for TSO headers, or PCI mapping"
" error\n");
dev_kfree_skb_any((struct sk_buff *)skb);
goto unwind;
stop:
rc2 = NETDEV_TX_BUSY;
/* Stop the queue if it wasn't stopped before. */
if (tx_queue->stopped == 1)
efx_stop_queue(tx_queue->efx);
unwind:
efx_enqueue_unwind(tx_queue);
return rc2;
}
/*
* Free up all TSO datastructures associated with tx_queue. This
* routine should be called only once the tx_queue is both empty and
* will no longer be used.
*/
static void efx_fini_tso(struct efx_tx_queue *tx_queue)
{
unsigned i;
if (tx_queue->buffer)
for (i = 0; i <= tx_queue->efx->type->txd_ring_mask; ++i)
efx_tsoh_free(tx_queue, &tx_queue->buffer[i]);
while (tx_queue->tso_headers_free != NULL)
efx_tsoh_block_free(tx_queue, tx_queue->tso_headers_free,
tx_queue->efx->pci_dev);
}

View File

@ -24,6 +24,10 @@
MDIO_MMDREG_DEVS0_PMAPMD | \ MDIO_MMDREG_DEVS0_PMAPMD | \
MDIO_MMDREG_DEVS0_PHYXS) MDIO_MMDREG_DEVS0_PHYXS)
#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
(1 << LOOPBACK_NETWORK))
/****************************************************************************/ /****************************************************************************/
/* Quake-specific MDIO registers */ /* Quake-specific MDIO registers */
#define MDIO_QUAKE_LED0_REG (0xD006) #define MDIO_QUAKE_LED0_REG (0xD006)
@ -35,6 +39,10 @@ void xfp_set_led(struct efx_nic *p, int led, int mode)
mode); mode);
} }
struct xfp_phy_data {
int tx_disabled;
};
#define XFP_MAX_RESET_TIME 500 #define XFP_MAX_RESET_TIME 500
#define XFP_RESET_WAIT 10 #define XFP_RESET_WAIT 10
@ -72,18 +80,31 @@ static int xfp_reset_phy(struct efx_nic *efx)
static int xfp_phy_init(struct efx_nic *efx) static int xfp_phy_init(struct efx_nic *efx)
{ {
struct xfp_phy_data *phy_data;
u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS); u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS);
int rc; int rc;
phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL);
efx->phy_data = (void *) phy_data;
EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision" EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision"
" %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid), " %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid),
MDIO_ID_REV(devid)); MDIO_ID_REV(devid));
phy_data->tx_disabled = efx->tx_disabled;
rc = xfp_reset_phy(efx); rc = xfp_reset_phy(efx);
EFX_INFO(efx, "XFP: PHY init %s.\n", EFX_INFO(efx, "XFP: PHY init %s.\n",
rc ? "failed" : "successful"); rc ? "failed" : "successful");
if (rc < 0)
goto fail;
return 0;
fail:
kfree(efx->phy_data);
efx->phy_data = NULL;
return rc; return rc;
} }
@ -110,6 +131,16 @@ static int xfp_phy_check_hw(struct efx_nic *efx)
static void xfp_phy_reconfigure(struct efx_nic *efx) static void xfp_phy_reconfigure(struct efx_nic *efx)
{ {
struct xfp_phy_data *phy_data = efx->phy_data;
/* Reset the PHY when moving from tx off to tx on */
if (phy_data->tx_disabled && !efx->tx_disabled)
xfp_reset_phy(efx);
mdio_clause45_transmit_disable(efx);
mdio_clause45_phy_reconfigure(efx);
phy_data->tx_disabled = efx->tx_disabled;
efx->link_up = xfp_link_ok(efx); efx->link_up = xfp_link_ok(efx);
efx->link_options = GM_LPA_10000FULL; efx->link_options = GM_LPA_10000FULL;
} }
@ -119,6 +150,10 @@ static void xfp_phy_fini(struct efx_nic *efx)
{ {
/* Clobber the LED if it was blinking */ /* Clobber the LED if it was blinking */
efx->board_info.blink(efx, 0); efx->board_info.blink(efx, 0);
/* Free the context block */
kfree(efx->phy_data);
efx->phy_data = NULL;
} }
struct efx_phy_operations falcon_xfp_phy_ops = { struct efx_phy_operations falcon_xfp_phy_ops = {
@ -129,4 +164,5 @@ struct efx_phy_operations falcon_xfp_phy_ops = {
.clear_interrupt = xfp_phy_clear_interrupt, .clear_interrupt = xfp_phy_clear_interrupt,
.reset_xaui = efx_port_dummy_op_void, .reset_xaui = efx_port_dummy_op_void,
.mmds = XFP_REQUIRED_DEVS, .mmds = XFP_REQUIRED_DEVS,
.loopbacks = XFP_LOOPBACKS,
}; };

View File

@ -1966,13 +1966,13 @@ struct sky2_status_le {
struct tx_ring_info { struct tx_ring_info {
struct sk_buff *skb; struct sk_buff *skb;
DECLARE_PCI_UNMAP_ADDR(mapaddr); DECLARE_PCI_UNMAP_ADDR(mapaddr);
DECLARE_PCI_UNMAP_ADDR(maplen); DECLARE_PCI_UNMAP_LEN(maplen);
}; };
struct rx_ring_info { struct rx_ring_info {
struct sk_buff *skb; struct sk_buff *skb;
dma_addr_t data_addr; dma_addr_t data_addr;
DECLARE_PCI_UNMAP_ADDR(data_size); DECLARE_PCI_UNMAP_LEN(data_size);
dma_addr_t frag_addr[ETH_JUMBO_MTU >> PAGE_SHIFT]; dma_addr_t frag_addr[ETH_JUMBO_MTU >> PAGE_SHIFT];
}; };

View File

@ -150,11 +150,9 @@ config HDLC_FR
config HDLC_PPP config HDLC_PPP
tristate "Synchronous Point-to-Point Protocol (PPP) support" tristate "Synchronous Point-to-Point Protocol (PPP) support"
depends on HDLC && BROKEN depends on HDLC
help help
Generic HDLC driver supporting PPP over WAN connections. Generic HDLC driver supporting PPP over WAN connections.
This module is currently broken and will cause a kernel panic
when a device configured in PPP mode is activated.
It will be replaced by new PPP implementation in Linux 2.6.26. It will be replaced by new PPP implementation in Linux 2.6.26.

View File

@ -629,7 +629,7 @@ static void sppp_channel_init(struct channel_data *chan)
d->base_addr = chan->cosa->datareg; d->base_addr = chan->cosa->datareg;
d->irq = chan->cosa->irq; d->irq = chan->cosa->irq;
d->dma = chan->cosa->dma; d->dma = chan->cosa->dma;
d->priv = chan; d->ml_priv = chan;
sppp_attach(&chan->pppdev); sppp_attach(&chan->pppdev);
if (register_netdev(d)) { if (register_netdev(d)) {
printk(KERN_WARNING "%s: register_netdev failed.\n", d->name); printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
@ -650,7 +650,7 @@ static void sppp_channel_delete(struct channel_data *chan)
static int cosa_sppp_open(struct net_device *d) static int cosa_sppp_open(struct net_device *d)
{ {
struct channel_data *chan = d->priv; struct channel_data *chan = d->ml_priv;
int err; int err;
unsigned long flags; unsigned long flags;
@ -690,7 +690,7 @@ static int cosa_sppp_open(struct net_device *d)
static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev) static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev)
{ {
struct channel_data *chan = dev->priv; struct channel_data *chan = dev->ml_priv;
netif_stop_queue(dev); netif_stop_queue(dev);
@ -701,7 +701,7 @@ static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev)
static void cosa_sppp_timeout(struct net_device *dev) static void cosa_sppp_timeout(struct net_device *dev)
{ {
struct channel_data *chan = dev->priv; struct channel_data *chan = dev->ml_priv;
if (test_bit(RXBIT, &chan->cosa->rxtx)) { if (test_bit(RXBIT, &chan->cosa->rxtx)) {
chan->stats.rx_errors++; chan->stats.rx_errors++;
@ -720,7 +720,7 @@ static void cosa_sppp_timeout(struct net_device *dev)
static int cosa_sppp_close(struct net_device *d) static int cosa_sppp_close(struct net_device *d)
{ {
struct channel_data *chan = d->priv; struct channel_data *chan = d->ml_priv;
unsigned long flags; unsigned long flags;
netif_stop_queue(d); netif_stop_queue(d);
@ -800,7 +800,7 @@ static int sppp_tx_done(struct channel_data *chan, int size)
static struct net_device_stats *cosa_net_stats(struct net_device *dev) static struct net_device_stats *cosa_net_stats(struct net_device *dev)
{ {
struct channel_data *chan = dev->priv; struct channel_data *chan = dev->ml_priv;
return &chan->stats; return &chan->stats;
} }
@ -1217,7 +1217,7 @@ static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd) int cmd)
{ {
int rv; int rv;
struct channel_data *chan = dev->priv; struct channel_data *chan = dev->ml_priv;
rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data); rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data);
if (rv == -ENOIOCTLCMD) { if (rv == -ENOIOCTLCMD) {
return sppp_do_ioctl(dev, ifr, cmd); return sppp_do_ioctl(dev, ifr, cmd);

View File

@ -45,7 +45,7 @@ static int ppp_open(struct net_device *dev)
int (*old_ioctl)(struct net_device *, struct ifreq *, int); int (*old_ioctl)(struct net_device *, struct ifreq *, int);
int result; int result;
dev->priv = &state(hdlc)->syncppp_ptr; dev->ml_priv = &state(hdlc)->syncppp_ptr;
state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev; state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev;
state(hdlc)->pppdev.dev = dev; state(hdlc)->pppdev.dev = dev;

View File

@ -75,7 +75,7 @@ static void hostess_input(struct z8530_channel *c, struct sk_buff *skb)
static int hostess_open(struct net_device *d) static int hostess_open(struct net_device *d)
{ {
struct sv11_device *sv11=d->priv; struct sv11_device *sv11=d->ml_priv;
int err = -1; int err = -1;
/* /*
@ -128,7 +128,7 @@ static int hostess_open(struct net_device *d)
static int hostess_close(struct net_device *d) static int hostess_close(struct net_device *d)
{ {
struct sv11_device *sv11=d->priv; struct sv11_device *sv11=d->ml_priv;
/* /*
* Discard new frames * Discard new frames
*/ */
@ -159,14 +159,14 @@ static int hostess_close(struct net_device *d)
static int hostess_ioctl(struct net_device *d, struct ifreq *ifr, int cmd) static int hostess_ioctl(struct net_device *d, struct ifreq *ifr, int cmd)
{ {
/* struct sv11_device *sv11=d->priv; /* struct sv11_device *sv11=d->ml_priv;
z8530_ioctl(d,&sv11->sync.chanA,ifr,cmd) */ z8530_ioctl(d,&sv11->sync.chanA,ifr,cmd) */
return sppp_do_ioctl(d, ifr,cmd); return sppp_do_ioctl(d, ifr,cmd);
} }
static struct net_device_stats *hostess_get_stats(struct net_device *d) static struct net_device_stats *hostess_get_stats(struct net_device *d)
{ {
struct sv11_device *sv11=d->priv; struct sv11_device *sv11=d->ml_priv;
if(sv11) if(sv11)
return z8530_get_stats(&sv11->sync.chanA); return z8530_get_stats(&sv11->sync.chanA);
else else
@ -179,7 +179,7 @@ static struct net_device_stats *hostess_get_stats(struct net_device *d)
static int hostess_queue_xmit(struct sk_buff *skb, struct net_device *d) static int hostess_queue_xmit(struct sk_buff *skb, struct net_device *d)
{ {
struct sv11_device *sv11=d->priv; struct sv11_device *sv11=d->ml_priv;
return z8530_queue_xmit(&sv11->sync.chanA, skb); return z8530_queue_xmit(&sv11->sync.chanA, skb);
} }
@ -325,6 +325,7 @@ static struct sv11_device *sv11_init(int iobase, int irq)
/* /*
* Initialise the PPP components * Initialise the PPP components
*/ */
d->ml_priv = sv;
sppp_attach(&sv->netdev); sppp_attach(&sv->netdev);
/* /*
@ -333,7 +334,6 @@ static struct sv11_device *sv11_init(int iobase, int irq)
d->base_addr = iobase; d->base_addr = iobase;
d->irq = irq; d->irq = irq;
d->priv = sv;
if(register_netdev(d)) if(register_netdev(d))
{ {

View File

@ -891,6 +891,7 @@ static int __devinit lmc_init_one(struct pci_dev *pdev,
/* Initialize the sppp layer */ /* Initialize the sppp layer */
/* An ioctl can cause a subsequent detach for raw frame interface */ /* An ioctl can cause a subsequent detach for raw frame interface */
dev->ml_priv = sc;
sc->if_type = LMC_PPP; sc->if_type = LMC_PPP;
sc->check = 0xBEAFCAFE; sc->check = 0xBEAFCAFE;
dev->base_addr = pci_resource_start(pdev, 0); dev->base_addr = pci_resource_start(pdev, 0);

View File

@ -241,6 +241,7 @@ static inline struct slvl_device *slvl_alloc(int iobase, int irq)
return NULL; return NULL;
sv = d->priv; sv = d->priv;
d->ml_priv = sv;
sv->if_ptr = &sv->pppdev; sv->if_ptr = &sv->pppdev;
sv->pppdev.dev = d; sv->pppdev.dev = d;
d->base_addr = iobase; d->base_addr = iobase;

View File

@ -666,7 +666,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
rx_status.flag = 0; rx_status.flag = 0;
rx_status.mactime = le64_to_cpu(rx_end->timestamp); rx_status.mactime = le64_to_cpu(rx_end->timestamp);
rx_status.freq = rx_status.freq =
ieee80211_frequency_to_channel(le16_to_cpu(rx_hdr->channel)); ieee80211_channel_to_frequency(le16_to_cpu(rx_hdr->channel));
rx_status.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? rx_status.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;

View File

@ -163,8 +163,8 @@ struct iwl4965_lq_sta {
struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
#endif #endif
struct iwl4965_rate dbg_fixed; struct iwl4965_rate dbg_fixed;
struct iwl_priv *drv;
#endif #endif
struct iwl_priv *drv;
}; };
static void rs_rate_scale_perform(struct iwl_priv *priv, static void rs_rate_scale_perform(struct iwl_priv *priv,

View File

@ -3978,7 +3978,7 @@ static void iwl4965_rx_reply_rx(struct iwl_priv *priv,
rx_status.mactime = le64_to_cpu(rx_start->timestamp); rx_status.mactime = le64_to_cpu(rx_start->timestamp);
rx_status.freq = rx_status.freq =
ieee80211_frequency_to_channel(le16_to_cpu(rx_start->channel)); ieee80211_channel_to_frequency(le16_to_cpu(rx_start->channel));
rx_status.band = (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? rx_status.band = (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
rx_status.rate_idx = rx_status.rate_idx =

View File

@ -388,8 +388,15 @@ islpci_open(struct net_device *ndev)
netif_start_queue(ndev); netif_start_queue(ndev);
/* Turn off carrier unless we know we have associated */ /* Turn off carrier if in STA or Ad-hoc mode. It will be turned on
netif_carrier_off(ndev); * once the firmware receives a trap of being associated
* (GEN_OID_LINKSTATE). In other modes (AP or WDS or monitor) we
* should just leave the carrier on as its expected the firmware
* won't send us a trigger. */
if (priv->iw_mode == IW_MODE_INFRA || priv->iw_mode == IW_MODE_ADHOC)
netif_carrier_off(ndev);
else
netif_carrier_on(ndev);
return 0; return 0;
} }

View File

@ -1032,8 +1032,10 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev)
* Initialize the device. * Initialize the device.
*/ */
status = rt2x00dev->ops->lib->initialize(rt2x00dev); status = rt2x00dev->ops->lib->initialize(rt2x00dev);
if (status) if (status) {
goto exit; rt2x00queue_uninitialize(rt2x00dev);
return status;
}
__set_bit(DEVICE_INITIALIZED, &rt2x00dev->flags); __set_bit(DEVICE_INITIALIZED, &rt2x00dev->flags);
@ -1043,11 +1045,6 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev)
rt2x00rfkill_register(rt2x00dev); rt2x00rfkill_register(rt2x00dev);
return 0; return 0;
exit:
rt2x00lib_uninitialize(rt2x00dev);
return status;
} }
int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) int rt2x00lib_start(struct rt2x00_dev *rt2x00dev)

View File

@ -314,13 +314,14 @@ int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev)
if (status) { if (status) {
ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n", ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n",
pci_dev->irq, status); pci_dev->irq, status);
return status; goto exit;
} }
return 0; return 0;
exit: exit:
rt2x00pci_uninitialize(rt2x00dev); queue_for_each(rt2x00dev, queue)
rt2x00pci_free_queue_dma(rt2x00dev, queue);
return status; return status;
} }

View File

@ -2366,6 +2366,7 @@ static int rt61pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
{ {
struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_dev *rt2x00dev = hw->priv;
struct rt2x00_intf *intf = vif_to_intf(control->vif); struct rt2x00_intf *intf = vif_to_intf(control->vif);
struct queue_entry_priv_pci_tx *priv_tx;
struct skb_frame_desc *skbdesc; struct skb_frame_desc *skbdesc;
unsigned int beacon_base; unsigned int beacon_base;
u32 reg; u32 reg;
@ -2373,21 +2374,8 @@ static int rt61pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
if (unlikely(!intf->beacon)) if (unlikely(!intf->beacon))
return -ENOBUFS; return -ENOBUFS;
/* priv_tx = intf->beacon->priv_data;
* We need to append the descriptor in front of the memset(priv_tx->desc, 0, intf->beacon->queue->desc_size);
* beacon frame.
*/
if (skb_headroom(skb) < intf->beacon->queue->desc_size) {
if (pskb_expand_head(skb, intf->beacon->queue->desc_size,
0, GFP_ATOMIC))
return -ENOMEM;
}
/*
* Add the descriptor in front of the skb.
*/
skb_push(skb, intf->beacon->queue->desc_size);
memset(skb->data, 0, intf->beacon->queue->desc_size);
/* /*
* Fill in skb descriptor * Fill in skb descriptor
@ -2395,9 +2383,9 @@ static int rt61pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
skbdesc = get_skb_frame_desc(skb); skbdesc = get_skb_frame_desc(skb);
memset(skbdesc, 0, sizeof(*skbdesc)); memset(skbdesc, 0, sizeof(*skbdesc));
skbdesc->flags |= FRAME_DESC_DRIVER_GENERATED; skbdesc->flags |= FRAME_DESC_DRIVER_GENERATED;
skbdesc->data = skb->data + intf->beacon->queue->desc_size; skbdesc->data = skb->data;
skbdesc->data_len = skb->len - intf->beacon->queue->desc_size; skbdesc->data_len = skb->len;
skbdesc->desc = skb->data; skbdesc->desc = priv_tx->desc;
skbdesc->desc_len = intf->beacon->queue->desc_size; skbdesc->desc_len = intf->beacon->queue->desc_size;
skbdesc->entry = intf->beacon; skbdesc->entry = intf->beacon;
@ -2425,7 +2413,10 @@ static int rt61pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb,
*/ */
beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx); beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx);
rt2x00pci_register_multiwrite(rt2x00dev, beacon_base, rt2x00pci_register_multiwrite(rt2x00dev, beacon_base,
skb->data, skb->len); skbdesc->desc, skbdesc->desc_len);
rt2x00pci_register_multiwrite(rt2x00dev,
beacon_base + skbdesc->desc_len,
skbdesc->data, skbdesc->data_len);
rt61pci_kick_tx_queue(rt2x00dev, control->queue); rt61pci_kick_tx_queue(rt2x00dev, control->queue);
return 0; return 0;
@ -2490,7 +2481,7 @@ static const struct data_queue_desc rt61pci_queue_tx = {
static const struct data_queue_desc rt61pci_queue_bcn = { static const struct data_queue_desc rt61pci_queue_bcn = {
.entry_num = 4 * BEACON_ENTRIES, .entry_num = 4 * BEACON_ENTRIES,
.data_size = MGMT_FRAME_SIZE, .data_size = 0, /* No DMA required for beacons */
.desc_size = TXINFO_SIZE, .desc_size = TXINFO_SIZE,
.priv_size = sizeof(struct queue_entry_priv_pci_tx), .priv_size = sizeof(struct queue_entry_priv_pci_tx),
}; };

View File

@ -908,9 +908,9 @@ static void wv_psa_show(psa_t * p)
p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5], p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5],
p->psa_call_code[6], p->psa_call_code[7]); p->psa_call_code[6], p->psa_call_code[7]);
#ifdef DEBUG_SHOW_UNUSED #ifdef DEBUG_SHOW_UNUSED
printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n", printk(KERN_DEBUG "psa_reserved[]: %02X:%02X\n",
p->psa_reserved[0], p->psa_reserved[0],
p->psa_reserved[1], p->psa_reserved[2], p->psa_reserved[3]); p->psa_reserved[1]);
#endif /* DEBUG_SHOW_UNUSED */ #endif /* DEBUG_SHOW_UNUSED */
printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);

View File

@ -1074,11 +1074,9 @@ wv_psa_show(psa_t * p)
p->psa_call_code[6], p->psa_call_code[6],
p->psa_call_code[7]); p->psa_call_code[7]);
#ifdef DEBUG_SHOW_UNUSED #ifdef DEBUG_SHOW_UNUSED
printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n", printk(KERN_DEBUG "psa_reserved[]: %02X:%02X\n",
p->psa_reserved[0], p->psa_reserved[0],
p->psa_reserved[1], p->psa_reserved[1]);
p->psa_reserved[2],
p->psa_reserved[3]);
#endif /* DEBUG_SHOW_UNUSED */ #endif /* DEBUG_SHOW_UNUSED */
printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);

View File

@ -889,9 +889,13 @@ static void tx_urb_complete(struct urb *urb)
} }
free_urb: free_urb:
skb = (struct sk_buff *)urb->context; skb = (struct sk_buff *)urb->context;
zd_mac_tx_to_dev(skb, urb->status); /*
* grab 'usb' pointer before handing off the skb (since
* it might be freed by zd_mac_tx_to_dev or mac80211)
*/
cb = (struct zd_tx_skb_control_block *)skb->cb; cb = (struct zd_tx_skb_control_block *)skb->cb;
usb = &zd_hw_mac(cb->hw)->chip.usb; usb = &zd_hw_mac(cb->hw)->chip.usb;
zd_mac_tx_to_dev(skb, urb->status);
free_tx_urb(usb, urb); free_tx_urb(usb, urb);
tx_dec_submitted_urbs(usb); tx_dec_submitted_urbs(usb);
return; return;

View File

@ -93,14 +93,16 @@ struct wireless_dev;
* used. * used.
*/ */
#if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR) #if defined(CONFIG_WLAN_80211) || defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#define LL_MAX_HEADER 32 # if defined(CONFIG_MAC80211_MESH)
# define LL_MAX_HEADER 128
# else
# define LL_MAX_HEADER 96
# endif
#elif defined(CONFIG_TR)
# define LL_MAX_HEADER 48
#else #else
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) # define LL_MAX_HEADER 32
#define LL_MAX_HEADER 96
#else
#define LL_MAX_HEADER 48
#endif
#endif #endif
#if !defined(CONFIG_NET_IPIP) && !defined(CONFIG_NET_IPIP_MODULE) && \ #if !defined(CONFIG_NET_IPIP) && !defined(CONFIG_NET_IPIP_MODULE) && \
@ -244,11 +246,16 @@ struct hh_cache
* *
* We could use other alignment values, but we must maintain the * We could use other alignment values, but we must maintain the
* relationship HH alignment <= LL alignment. * relationship HH alignment <= LL alignment.
*
* LL_ALLOCATED_SPACE also takes into account the tailroom the device
* may need.
*/ */
#define LL_RESERVED_SPACE(dev) \ #define LL_RESERVED_SPACE(dev) \
(((dev)->hard_header_len&~(HH_DATA_MOD - 1)) + HH_DATA_MOD) ((((dev)->hard_header_len+(dev)->needed_headroom)&~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
#define LL_RESERVED_SPACE_EXTRA(dev,extra) \ #define LL_RESERVED_SPACE_EXTRA(dev,extra) \
((((dev)->hard_header_len+extra)&~(HH_DATA_MOD - 1)) + HH_DATA_MOD) ((((dev)->hard_header_len+(dev)->needed_headroom+(extra))&~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
#define LL_ALLOCATED_SPACE(dev) \
((((dev)->hard_header_len+(dev)->needed_headroom+(dev)->needed_tailroom)&~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
struct header_ops { struct header_ops {
int (*create) (struct sk_buff *skb, struct net_device *dev, int (*create) (struct sk_buff *skb, struct net_device *dev,
@ -567,6 +574,13 @@ struct net_device
unsigned short type; /* interface hardware type */ unsigned short type; /* interface hardware type */
unsigned short hard_header_len; /* hardware hdr length */ unsigned short hard_header_len; /* hardware hdr length */
/* extra head- and tailroom the hardware may need, but not in all cases
* can this be guaranteed, especially tailroom. Some cases also use
* LL_MAX_HEADER instead to allocate the skb.
*/
unsigned short needed_headroom;
unsigned short needed_tailroom;
struct net_device *master; /* Pointer to master device of a group, struct net_device *master; /* Pointer to master device of a group,
* which this device is member of. * which this device is member of.
*/ */
@ -715,6 +729,9 @@ struct net_device
struct net *nd_net; struct net *nd_net;
#endif #endif
/* mid-layer private */
void *ml_priv;
/* bridge stuff */ /* bridge stuff */
struct net_bridge_port *br_port; struct net_bridge_port *br_port;
/* macvlan */ /* macvlan */

View File

@ -57,9 +57,6 @@ typedef union {
__u8 byte[2]; __u8 byte[2];
} __u16_host_order; } __u16_host_order;
/* Same purpose, different application */
#define u16ho(array) (* ((__u16 *) array))
/* Types of discovery */ /* Types of discovery */
typedef enum { typedef enum {
DISCOVERY_LOG, /* What's in our discovery log */ DISCOVERY_LOG, /* What's in our discovery log */

View File

@ -59,7 +59,7 @@ struct ppp_device
static inline struct sppp *sppp_of(struct net_device *dev) static inline struct sppp *sppp_of(struct net_device *dev)
{ {
struct ppp_device **ppp = dev->priv; struct ppp_device **ppp = dev->ml_priv;
BUG_ON((*ppp)->dev != dev); BUG_ON((*ppp)->dev != dev);
return &(*ppp)->sppp; return &(*ppp)->sppp;
} }

View File

@ -419,7 +419,7 @@ static void arp_reply(struct sk_buff *skb)
return; return;
size = arp_hdr_len(skb->dev); size = arp_hdr_len(skb->dev);
send_skb = find_skb(np, size + LL_RESERVED_SPACE(np->dev), send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev),
LL_RESERVED_SPACE(np->dev)); LL_RESERVED_SPACE(np->dev));
if (!send_skb) if (!send_skb)

View File

@ -270,7 +270,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
int err = 0; int err = 0;
int skb_len; int skb_len;
/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces /* Cast sk->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK number of warnings when compiling with -W --ANK
*/ */
if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=

View File

@ -340,7 +340,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
dev_hold(dev); dev_hold(dev);
skb = sock_alloc_send_skb(sk, len+LL_RESERVED_SPACE(dev), skb = sock_alloc_send_skb(sk, len+LL_ALLOCATED_SPACE(dev),
msg->msg_flags & MSG_DONTWAIT, &err); msg->msg_flags & MSG_DONTWAIT, &err);
if (skb==NULL) if (skb==NULL)
goto out_unlock; goto out_unlock;

View File

@ -570,7 +570,7 @@ struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
* Allocate a buffer * Allocate a buffer
*/ */
skb = alloc_skb(arp_hdr_len(dev) + LL_RESERVED_SPACE(dev), GFP_ATOMIC); skb = alloc_skb(arp_hdr_len(dev) + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
if (skb == NULL) if (skb == NULL)
return NULL; return NULL;

View File

@ -338,7 +338,7 @@ static int cipso_v4_cache_check(const unsigned char *key,
return -ENOENT; return -ENOENT;
hash = cipso_v4_map_cache_hash(key, key_len); hash = cipso_v4_map_cache_hash(key, key_len);
bkt = hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); bkt = hash & (CIPSO_V4_CACHE_BUCKETS - 1);
spin_lock_bh(&cipso_v4_cache[bkt].lock); spin_lock_bh(&cipso_v4_cache[bkt].lock);
list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) { list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) {
if (entry->hash == hash && if (entry->hash == hash &&
@ -417,7 +417,7 @@ int cipso_v4_cache_add(const struct sk_buff *skb,
atomic_inc(&secattr->cache->refcount); atomic_inc(&secattr->cache->refcount);
entry->lsm_data = secattr->cache; entry->lsm_data = secattr->cache;
bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETS - 1);
spin_lock_bh(&cipso_v4_cache[bkt].lock); spin_lock_bh(&cipso_v4_cache[bkt].lock);
if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) { if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) {
list_add(&entry->list, &cipso_v4_cache[bkt].list); list_add(&entry->list, &cipso_v4_cache[bkt].list);

View File

@ -292,7 +292,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
struct iphdr *pip; struct iphdr *pip;
struct igmpv3_report *pig; struct igmpv3_report *pig;
skb = alloc_skb(size + LL_RESERVED_SPACE(dev), GFP_ATOMIC); skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
if (skb == NULL) if (skb == NULL)
return NULL; return NULL;
@ -653,7 +653,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
return -1; return -1;
} }
skb=alloc_skb(IGMP_SIZE+LL_RESERVED_SPACE(dev), GFP_ATOMIC); skb=alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
if (skb == NULL) { if (skb == NULL) {
ip_rt_put(rt); ip_rt_put(rt);
return -1; return -1;

View File

@ -710,14 +710,14 @@ static void __init ic_bootp_send_if(struct ic_device *d, unsigned long jiffies_d
struct net_device *dev = d->dev; struct net_device *dev = d->dev;
struct sk_buff *skb; struct sk_buff *skb;
struct bootp_pkt *b; struct bootp_pkt *b;
int hh_len = LL_RESERVED_SPACE(dev);
struct iphdr *h; struct iphdr *h;
/* Allocate packet */ /* Allocate packet */
skb = alloc_skb(sizeof(struct bootp_pkt) + hh_len + 15, GFP_KERNEL); skb = alloc_skb(sizeof(struct bootp_pkt) + LL_ALLOCATED_SPACE(dev) + 15,
GFP_KERNEL);
if (!skb) if (!skb)
return; return;
skb_reserve(skb, hh_len); skb_reserve(skb, LL_RESERVED_SPACE(dev));
b = (struct bootp_pkt *) skb_put(skb, sizeof(struct bootp_pkt)); b = (struct bootp_pkt *) skb_put(skb, sizeof(struct bootp_pkt));
memset(b, 0, sizeof(struct bootp_pkt)); memset(b, 0, sizeof(struct bootp_pkt));

View File

@ -322,7 +322,6 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
unsigned int flags) unsigned int flags)
{ {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
int hh_len;
struct iphdr *iph; struct iphdr *iph;
struct sk_buff *skb; struct sk_buff *skb;
unsigned int iphlen; unsigned int iphlen;
@ -336,13 +335,12 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
if (flags&MSG_PROBE) if (flags&MSG_PROBE)
goto out; goto out;
hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); skb = sock_alloc_send_skb(sk,
length + LL_ALLOCATED_SPACE(rt->u.dst.dev) + 15,
skb = sock_alloc_send_skb(sk, length+hh_len+15, flags & MSG_DONTWAIT, &err);
flags&MSG_DONTWAIT, &err);
if (skb == NULL) if (skb == NULL)
goto error; goto error;
skb_reserve(skb, hh_len); skb_reserve(skb, LL_RESERVED_SPACE(rt->u.dst.dev));
skb->priority = sk->sk_priority; skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark; skb->mark = sk->sk_mark;

View File

@ -1842,9 +1842,16 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
} }
/* Don't lost mark skbs that were fwd transmitted after RTO */ /* Marking forward transmissions that were made after RTO lost
if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) && * can cause unnecessary retransmissions in some scenarios,
!after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark)) { * SACK blocks will mitigate that in some but not in all cases.
* We used to not mark them but it was causing break-ups with
* receivers that do only in-order receival.
*
* TODO: we could detect presence of such receiver and select
* different behavior per flow.
*/
if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
tp->lost_out += tcp_skb_pcount(skb); tp->lost_out += tcp_skb_pcount(skb);
} }
@ -1860,7 +1867,7 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
tp->reordering = min_t(unsigned int, tp->reordering, tp->reordering = min_t(unsigned int, tp->reordering,
sysctl_tcp_reordering); sysctl_tcp_reordering);
tcp_set_ca_state(sk, TCP_CA_Loss); tcp_set_ca_state(sk, TCP_CA_Loss);
tp->high_seq = tp->frto_highmark; tp->high_seq = tp->snd_nxt;
TCP_ECN_queue_cwr(tp); TCP_ECN_queue_cwr(tp);
tcp_clear_retrans_hints_partial(tp); tcp_clear_retrans_hints_partial(tp);
@ -2482,7 +2489,7 @@ static void tcp_try_to_open(struct sock *sk, int flag)
tcp_verify_left_out(tp); tcp_verify_left_out(tp);
if (tp->retrans_out == 0) if (!tp->frto_counter && tp->retrans_out == 0)
tp->retrans_stamp = 0; tp->retrans_stamp = 0;
if (flag & FLAG_ECE) if (flag & FLAG_ECE)

View File

@ -780,7 +780,7 @@ slow_path:
* Allocate buffer. * Allocate buffer.
*/ */
if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_RESERVED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) { if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_ALLOCATED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) {
NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n"); NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n");
IP6_INC_STATS(ip6_dst_idev(skb->dst), IP6_INC_STATS(ip6_dst_idev(skb->dst),
IPSTATS_MIB_FRAGFAILS); IPSTATS_MIB_FRAGFAILS);

View File

@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
IPV6_TLV_PADN, 0 }; IPV6_TLV_PADN, 0 };
/* we assume size > sizeof(ra) here */ /* we assume size > sizeof(ra) here */
skb = sock_alloc_send_skb(sk, size + LL_RESERVED_SPACE(dev), 1, &err); skb = sock_alloc_send_skb(sk, size + LL_ALLOCATED_SPACE(dev), 1, &err);
if (!skb) if (!skb)
return NULL; return NULL;
@ -1790,7 +1790,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
payload_len = len + sizeof(ra); payload_len = len + sizeof(ra);
full_len = sizeof(struct ipv6hdr) + payload_len; full_len = sizeof(struct ipv6hdr) + payload_len;
skb = sock_alloc_send_skb(sk, LL_RESERVED_SPACE(dev) + full_len, 1, &err); skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + full_len, 1, &err);
if (skb == NULL) { if (skb == NULL) {
rcu_read_lock(); rcu_read_lock();

View File

@ -479,7 +479,7 @@ static void __ndisc_send(struct net_device *dev,
skb = sock_alloc_send_skb(sk, skb = sock_alloc_send_skb(sk,
(MAX_HEADER + sizeof(struct ipv6hdr) + (MAX_HEADER + sizeof(struct ipv6hdr) +
len + LL_RESERVED_SPACE(dev)), len + LL_ALLOCATED_SPACE(dev)),
1, &err); 1, &err);
if (!skb) { if (!skb) {
ND_PRINTK0(KERN_ERR ND_PRINTK0(KERN_ERR
@ -1521,7 +1521,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
buff = sock_alloc_send_skb(sk, buff = sock_alloc_send_skb(sk,
(MAX_HEADER + sizeof(struct ipv6hdr) + (MAX_HEADER + sizeof(struct ipv6hdr) +
len + LL_RESERVED_SPACE(dev)), len + LL_ALLOCATED_SPACE(dev)),
1, &err); 1, &err);
if (buff == NULL) { if (buff == NULL) {
ND_PRINTK0(KERN_ERR ND_PRINTK0(KERN_ERR

View File

@ -609,7 +609,6 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6hdr *iph; struct ipv6hdr *iph;
struct sk_buff *skb; struct sk_buff *skb;
unsigned int hh_len;
int err; int err;
if (length > rt->u.dst.dev->mtu) { if (length > rt->u.dst.dev->mtu) {
@ -619,13 +618,12 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
if (flags&MSG_PROBE) if (flags&MSG_PROBE)
goto out; goto out;
hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); skb = sock_alloc_send_skb(sk,
length + LL_ALLOCATED_SPACE(rt->u.dst.dev) + 15,
skb = sock_alloc_send_skb(sk, length+hh_len+15, flags & MSG_DONTWAIT, &err);
flags&MSG_DONTWAIT, &err);
if (skb == NULL) if (skb == NULL)
goto error; goto error;
skb_reserve(skb, hh_len); skb_reserve(skb, LL_RESERVED_SPACE(rt->u.dst.dev));
skb->priority = sk->sk_priority; skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark; skb->mark = sk->sk_mark;

View File

@ -40,6 +40,8 @@
#include <net/irda/discovery.h> #include <net/irda/discovery.h>
#include <asm/unaligned.h>
/* /*
* Function irlmp_add_discovery (cachelog, discovery) * Function irlmp_add_discovery (cachelog, discovery)
* *
@ -87,7 +89,7 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
*/ */
hashbin_remove_this(cachelog, (irda_queue_t *) node); hashbin_remove_this(cachelog, (irda_queue_t *) node);
/* Check if hints bits are unchanged */ /* Check if hints bits are unchanged */
if(u16ho(node->data.hints) == u16ho(new->data.hints)) if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints))
/* Set time of first discovery for this node */ /* Set time of first discovery for this node */
new->firststamp = node->firststamp; new->firststamp = node->firststamp;
kfree(node); kfree(node);
@ -281,9 +283,9 @@ struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
/* Mask out the ones we don't want : /* Mask out the ones we don't want :
* We want to match the discovery mask, and to get only * We want to match the discovery mask, and to get only
* the most recent one (unless we want old ones) */ * the most recent one (unless we want old ones) */
if ((u16ho(discovery->data.hints) & mask) && if ((get_unaligned((__u16 *)discovery->data.hints) & mask) &&
((old_entries) || ((old_entries) ||
((jiffies - discovery->firststamp) < j_timeout)) ) { ((jiffies - discovery->firststamp) < j_timeout))) {
/* Create buffer as needed. /* Create buffer as needed.
* As this function get called a lot and most time * As this function get called a lot and most time
* we don't have anything to put in the log (we are * we don't have anything to put in the log (we are

View File

@ -1062,7 +1062,8 @@ void irlmp_discovery_expiry(discinfo_t *expiries, int number)
for(i = 0; i < number; i++) { for(i = 0; i < number; i++) {
/* Check if we should notify client */ /* Check if we should notify client */
if ((client->expir_callback) && if ((client->expir_callback) &&
(client->hint_mask.word & u16ho(expiries[i].hints) (client->hint_mask.word &
get_unaligned((__u16 *)expiries[i].hints)
& 0x7f7f) ) & 0x7f7f) )
client->expir_callback(&(expiries[i]), client->expir_callback(&(expiries[i]),
EXPIRY_TIMEOUT, EXPIRY_TIMEOUT,
@ -1086,7 +1087,7 @@ discovery_t *irlmp_get_discovery_response(void)
IRDA_ASSERT(irlmp != NULL, return NULL;); IRDA_ASSERT(irlmp != NULL, return NULL;);
u16ho(irlmp->discovery_rsp.data.hints) = irlmp->hints.word; put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints);
/* /*
* Set character set for device name (we use ASCII), and * Set character set for device name (we use ASCII), and

View File

@ -1673,7 +1673,7 @@ irnet_discovery_indication(discinfo_t * discovery,
/* Notify the control channel */ /* Notify the control channel */
irnet_post_event(NULL, IRNET_DISCOVER, irnet_post_event(NULL, IRNET_DISCOVER,
discovery->saddr, discovery->daddr, discovery->info, discovery->saddr, discovery->daddr, discovery->info,
u16ho(discovery->hints)); get_unaligned((__u16 *)discovery->hints));
DEXIT(IRDA_OCB_TRACE, "\n"); DEXIT(IRDA_OCB_TRACE, "\n");
} }
@ -1704,7 +1704,7 @@ irnet_expiry_indication(discinfo_t * expiry,
/* Notify the control channel */ /* Notify the control channel */
irnet_post_event(NULL, IRNET_EXPIRE, irnet_post_event(NULL, IRNET_EXPIRE,
expiry->saddr, expiry->daddr, expiry->info, expiry->saddr, expiry->daddr, expiry->info,
u16ho(expiry->hints)); get_unaligned((__u16 *)expiry->hints));
DEXIT(IRDA_OCB_TRACE, "\n"); DEXIT(IRDA_OCB_TRACE, "\n");
} }

View File

@ -255,14 +255,23 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata) void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
{ {
char buf[50]; char buf[50];
struct ieee80211_key *key;
if (!sdata->debugfsdir) if (!sdata->debugfsdir)
return; return;
sprintf(buf, "../keys/%d", sdata->default_key->debugfs.cnt); /* this is running under the key lock */
sdata->debugfs.default_key =
debugfs_create_symlink("default_key", sdata->debugfsdir, buf); key = sdata->default_key;
if (key) {
sprintf(buf, "../keys/%d", key->debugfs.cnt);
sdata->debugfs.default_key =
debugfs_create_symlink("default_key",
sdata->debugfsdir, buf);
} else
ieee80211_debugfs_key_remove_default(sdata);
} }
void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata)
{ {
if (!sdata) if (!sdata)

View File

@ -54,6 +54,15 @@ int ieee80211_if_add(struct net_device *dev, const char *name,
if (!ndev) if (!ndev)
return -ENOMEM; return -ENOMEM;
ndev->needed_headroom = local->tx_headroom +
4*6 /* four MAC addresses */
+ 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */
+ 6 /* mesh */
+ 8 /* rfc1042/bridge tunnel */
- ETH_HLEN /* ethernet hard_header_len */
+ IEEE80211_ENCRYPT_HEADROOM;
ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM;
ret = dev_alloc_name(ndev, ndev->name); ret = dev_alloc_name(ndev, ndev->name);
if (ret < 0) if (ret < 0)
goto fail; goto fail;

View File

@ -397,7 +397,7 @@ int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
put_unaligned(cpu_to_le32(sdata->u.sta.mesh_seqnum), &meshhdr->seqnum); put_unaligned(cpu_to_le32(sdata->u.sta.mesh_seqnum), &meshhdr->seqnum);
sdata->u.sta.mesh_seqnum++; sdata->u.sta.mesh_seqnum++;
return 5; return 6;
} }
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)

View File

@ -120,7 +120,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
*pos++ = WLAN_EID_PREP; *pos++ = WLAN_EID_PREP;
break; break;
default: default:
kfree(skb); kfree_skb(skb);
return -ENOTSUPP; return -ENOTSUPP;
break; break;
} }

View File

@ -158,19 +158,25 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0) if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0)
return -ENOSPC; return -ENOSPC;
read_lock(&pathtbl_resize_lock);
new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
if (!new_mpath) { if (!new_mpath) {
atomic_dec(&sdata->u.sta.mpaths); atomic_dec(&sdata->u.sta.mpaths);
err = -ENOMEM; err = -ENOMEM;
goto endadd2; goto endadd2;
} }
new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
if (!new_node) {
kfree(new_mpath);
atomic_dec(&sdata->u.sta.mpaths);
err = -ENOMEM;
goto endadd2;
}
read_lock(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN); memcpy(new_mpath->dst, dst, ETH_ALEN);
new_mpath->dev = dev; new_mpath->dev = dev;
new_mpath->flags = 0; new_mpath->flags = 0;
skb_queue_head_init(&new_mpath->frame_queue); skb_queue_head_init(&new_mpath->frame_queue);
new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
new_node->mpath = new_mpath; new_node->mpath = new_mpath;
new_mpath->timer.data = (unsigned long) new_mpath; new_mpath->timer.data = (unsigned long) new_mpath;
new_mpath->timer.function = mesh_path_timer; new_mpath->timer.function = mesh_path_timer;
@ -202,7 +208,6 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
endadd: endadd:
spin_unlock(&mesh_paths->hashwlock[hash_idx]); spin_unlock(&mesh_paths->hashwlock[hash_idx]);
endadd2:
read_unlock(&pathtbl_resize_lock); read_unlock(&pathtbl_resize_lock);
if (!err && grow) { if (!err && grow) {
struct mesh_table *oldtbl, *newtbl; struct mesh_table *oldtbl, *newtbl;
@ -215,10 +220,12 @@ endadd2:
return -ENOMEM; return -ENOMEM;
} }
rcu_assign_pointer(mesh_paths, newtbl); rcu_assign_pointer(mesh_paths, newtbl);
write_unlock(&pathtbl_resize_lock);
synchronize_rcu(); synchronize_rcu();
mesh_table_free(oldtbl, false); mesh_table_free(oldtbl, false);
write_unlock(&pathtbl_resize_lock);
} }
endadd2:
return err; return err;
} }

View File

@ -665,6 +665,26 @@ static void ieee80211_authenticate(struct net_device *dev,
mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT); mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
} }
static int ieee80211_compatible_rates(struct ieee80211_sta_bss *bss,
struct ieee80211_supported_band *sband,
u64 *rates)
{
int i, j, count;
*rates = 0;
count = 0;
for (i = 0; i < bss->supp_rates_len; i++) {
int rate = (bss->supp_rates[i] & 0x7F) * 5;
for (j = 0; j < sband->n_bitrates; j++)
if (sband->bitrates[j].bitrate == rate) {
*rates |= BIT(j);
count++;
break;
}
}
return count;
}
static void ieee80211_send_assoc(struct net_device *dev, static void ieee80211_send_assoc(struct net_device *dev,
struct ieee80211_if_sta *ifsta) struct ieee80211_if_sta *ifsta)
@ -673,11 +693,12 @@ static void ieee80211_send_assoc(struct net_device *dev,
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
u8 *pos, *ies; u8 *pos, *ies;
int i, len; int i, len, count, rates_len, supp_rates_len;
u16 capab; u16 capab;
struct ieee80211_sta_bss *bss; struct ieee80211_sta_bss *bss;
int wmm = 0; int wmm = 0;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u64 rates = 0;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 200 + ifsta->extra_ie_len + sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
@ -740,24 +761,39 @@ static void ieee80211_send_assoc(struct net_device *dev,
*pos++ = ifsta->ssid_len; *pos++ = ifsta->ssid_len;
memcpy(pos, ifsta->ssid, ifsta->ssid_len); memcpy(pos, ifsta->ssid, ifsta->ssid_len);
len = sband->n_bitrates; /* all supported rates should be added here but some APs
if (len > 8) * (e.g. D-Link DAP 1353 in b-only mode) don't like that
len = 8; * Therefore only add rates the AP supports */
pos = skb_put(skb, len + 2); rates_len = ieee80211_compatible_rates(bss, sband, &rates);
*pos++ = WLAN_EID_SUPP_RATES; supp_rates_len = rates_len;
*pos++ = len; if (supp_rates_len > 8)
for (i = 0; i < len; i++) { supp_rates_len = 8;
int rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5);
}
if (sband->n_bitrates > len) { len = sband->n_bitrates;
pos = skb_put(skb, sband->n_bitrates - len + 2); pos = skb_put(skb, supp_rates_len + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = WLAN_EID_SUPP_RATES;
*pos++ = sband->n_bitrates - len; *pos++ = supp_rates_len;
for (i = len; i < sband->n_bitrates; i++) {
count = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) {
int rate = sband->bitrates[i].bitrate; int rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5); *pos++ = (u8) (rate / 5);
if (++count == 8)
break;
}
}
if (count == 8) {
pos = skb_put(skb, rates_len - count + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = rates_len - count;
for (i++; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) {
int rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5);
}
} }
} }

View File

@ -1305,11 +1305,11 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
if (is_multicast_ether_addr(skb->data)) { if (is_multicast_ether_addr(skb->data)) {
if (*mesh_ttl > 0) { if (*mesh_ttl > 0) {
xmit_skb = skb_copy(skb, GFP_ATOMIC); xmit_skb = skb_copy(skb, GFP_ATOMIC);
if (!xmit_skb && net_ratelimit()) if (xmit_skb)
xmit_skb->pkt_type = PACKET_OTHERHOST;
else if (net_ratelimit())
printk(KERN_DEBUG "%s: failed to clone " printk(KERN_DEBUG "%s: failed to clone "
"multicast frame\n", dev->name); "multicast frame\n", dev->name);
else
xmit_skb->pkt_type = PACKET_OTHERHOST;
} else } else
IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta, IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
dropped_frames_ttl); dropped_frames_ttl);
@ -1395,7 +1395,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
padding = ((4 - subframe_len) & 0x3); padding = ((4 - subframe_len) & 0x3);
/* the last MSDU has no padding */ /* the last MSDU has no padding */
if (subframe_len > remaining) { if (subframe_len > remaining) {
printk(KERN_DEBUG "%s: wrong buffer size", dev->name); printk(KERN_DEBUG "%s: wrong buffer size\n", dev->name);
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
@ -1418,7 +1418,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
eth = (struct ethhdr *) skb_pull(skb, ntohs(len) + eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
padding); padding);
if (!eth) { if (!eth) {
printk(KERN_DEBUG "%s: wrong buffer size ", printk(KERN_DEBUG "%s: wrong buffer size\n",
dev->name); dev->name);
dev_kfree_skb(frame); dev_kfree_skb(frame);
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
@ -1952,7 +1952,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (!skb_new) { if (!skb_new) {
if (net_ratelimit()) if (net_ratelimit())
printk(KERN_DEBUG "%s: failed to copy " printk(KERN_DEBUG "%s: failed to copy "
"multicast frame for %s", "multicast frame for %s\n",
wiphy_name(local->hw.wiphy), wiphy_name(local->hw.wiphy),
prev->dev->name); prev->dev->name);
continue; continue;

View File

@ -1562,13 +1562,13 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
* be cloned. This could happen, e.g., with Linux bridge code passing * be cloned. This could happen, e.g., with Linux bridge code passing
* us broadcast frames. */ * us broadcast frames. */
if (head_need > 0 || skb_cloned(skb)) { if (head_need > 0 || skb_header_cloned(skb)) {
#if 0 #if 0
printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes " printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
"of headroom\n", dev->name, head_need); "of headroom\n", dev->name, head_need);
#endif #endif
if (skb_cloned(skb)) if (skb_header_cloned(skb))
I802_DEBUG_INC(local->tx_expand_skb_head_cloned); I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
else else
I802_DEBUG_INC(local->tx_expand_skb_head); I802_DEBUG_INC(local->tx_expand_skb_head);
@ -1898,6 +1898,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE; control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
control->flags |= IEEE80211_TXCTL_NO_ACK; control->flags |= IEEE80211_TXCTL_NO_ACK;
control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
control->retry_limit = 1; control->retry_limit = 1;
control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT; control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
} }

View File

@ -153,15 +153,15 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
/* 7.1.3.5a.2 */ /* 7.1.3.5a.2 */
switch (ae) { switch (ae) {
case 0: case 0:
return 5; return 6;
case 1: case 1:
return 11; return 12;
case 2: case 2:
return 17; return 18;
case 3: case 3:
return 23; return 24;
default: default:
return 5; return 6;
} }
} }

View File

@ -394,7 +394,8 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt)
qd->handle); qd->handle);
if (!q->queues[i]) { if (!q->queues[i]) {
q->queues[i] = &noop_qdisc; q->queues[i] = &noop_qdisc;
printk(KERN_ERR "%s child qdisc %i creation failed", dev->name, i); printk(KERN_ERR "%s child qdisc %i creation failed\n",
dev->name, i);
} }
} }

View File

@ -472,6 +472,9 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
goto nla_put_failure; goto nla_put_failure;
nla_nest_end(skb, nest_parms); nla_nest_end(skb, nest_parms);
if (ctnetlink_dump_id(skb, ct) < 0)
goto nla_put_failure;
if (events & IPCT_DESTROY) { if (events & IPCT_DESTROY) {
if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)

View File

@ -179,3 +179,5 @@ module_exit(iprange_mt_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>, Jan Engelhardt <jengelh@computergmbh.de>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>, Jan Engelhardt <jengelh@computergmbh.de>");
MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching"); MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching");
MODULE_ALIAS("ipt_iprange");
MODULE_ALIAS("ip6t_iprange");

View File

@ -743,7 +743,7 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
if (len > dev->mtu+reserve) if (len > dev->mtu+reserve)
goto out_unlock; goto out_unlock;
skb = sock_alloc_send_skb(sk, len + LL_RESERVED_SPACE(dev), skb = sock_alloc_send_skb(sk, len + LL_ALLOCATED_SPACE(dev),
msg->msg_flags & MSG_DONTWAIT, &err); msg->msg_flags & MSG_DONTWAIT, &err);
if (skb==NULL) if (skb==NULL)
goto out_unlock; goto out_unlock;

View File

@ -2418,7 +2418,8 @@ static int sctp_process_param(struct sctp_association *asoc,
break; break;
case SCTP_PARAM_IPV6_ADDRESS: case SCTP_PARAM_IPV6_ADDRESS:
asoc->peer.ipv6_address = 1; if (PF_INET6 == asoc->base.sk->sk_family)
asoc->peer.ipv6_address = 1;
break; break;
case SCTP_PARAM_HOST_NAME_ADDRESS: case SCTP_PARAM_HOST_NAME_ADDRESS:
@ -2829,6 +2830,19 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
addr_param = (union sctp_addr_param *) addr_param = (union sctp_addr_param *)
((void *)asconf_param + sizeof(sctp_addip_param_t)); ((void *)asconf_param + sizeof(sctp_addip_param_t));
switch (addr_param->v4.param_hdr.type) {
case SCTP_PARAM_IPV6_ADDRESS:
if (!asoc->peer.ipv6_address)
return SCTP_ERROR_INV_PARAM;
break;
case SCTP_PARAM_IPV4_ADDRESS:
if (!asoc->peer.ipv4_address)
return SCTP_ERROR_INV_PARAM;
break;
default:
return SCTP_ERROR_INV_PARAM;
}
af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type));
if (unlikely(!af)) if (unlikely(!af))
return SCTP_ERROR_INV_PARAM; return SCTP_ERROR_INV_PARAM;

View File

@ -25,11 +25,11 @@ static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
struct dst_entry *dst = skb->dst; struct dst_entry *dst = skb->dst;
int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev) int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev)
- skb_headroom(skb); - skb_headroom(skb);
int ntail = dst->dev->needed_tailroom - skb_tailroom(skb);
if (nhead > 0) if (nhead > 0 || ntail > 0)
return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC);
/* Check tail too... */
return 0; return 0;
} }