CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
# CONFIG_BLK_DEV_BSG is not set
+ CONFIG_ARCH_MULTI_V6=y
CONFIG_ARCH_OMAP2PLUS=y
CONFIG_OMAP_RESET_CLOCKS=y
CONFIG_OMAP_MUX_DEBUG=y
CONFIG_TI_DAVINCI_MDIO=y
CONFIG_TI_DAVINCI_CPDMA=y
CONFIG_TI_CPSW=y
+CONFIG_AT803X_PHY=y
int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp);
/* Queue pointer cannot be re-set on an fp-basis, as moving pointer
- * backward along the array could cause memory to be overriden
+ * backward along the array could cause memory to be overridden
*/
for (cos = 1; cos < bp->max_cos; cos++) {
for (i = 0; i < old_eth_num - delta; i++) {
dma_unmap_single(&bp->pdev->dev, BD_UNMAP_ADDR(tx_start_bd),
BD_UNMAP_LEN(tx_start_bd), DMA_TO_DEVICE);
-
nbd = le16_to_cpu(tx_start_bd->nbd) - 1;
#ifdef BNX2X_STOP_ON_ERROR
if ((nbd - 1) > (MAX_SKB_FRAGS + 2)) {
smp_mb();
if (unlikely(netif_tx_queue_stopped(txq))) {
- /* Taking tx_lock() is needed to prevent reenabling the queue
+ /* Taking tx_lock() is needed to prevent re-enabling the queue
* while it's empty. This could have happen if rx_action() gets
* suspended in bnx2x_tx_int() after the condition before
* netif_tx_wake_queue(), while tx_action (bnx2x_start_xmit()):
return err;
}
- /* Unmap the page as we r going to pass it to the stack */
+ /* Unmap the page as we're going to pass it to the stack */
dma_unmap_page(&bp->pdev->dev,
dma_unmap_addr(&old_rx_pg, mapping),
SGE_PAGES, DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
}
-
/* put new data in bin */
rx_buf->data = new_data;
cqe_fp);
goto next_rx;
-
}
queue = cqe->end_agg_cqe.queue_index;
tpa_info = &fp->tpa_info[queue];
le16_to_cpu(cqe_fp->vlan_tag));
napi_gro_receive(&fp->napi, skb);
-
next_rx:
rx_buf->data = NULL;
memset(data, 0, sizeof(*data));
- /* Fill the report data: efective line speed */
+ /* Fill the report data: effective line speed */
data->line_speed = line_speed;
/* Link is down */
*
* @bp: driver handle
*
- * None atomic inmlementation.
+ * None atomic implementation.
* Should be called under the phy_lock.
*/
void __bnx2x_link_report(struct bnx2x *bp)
"mtu %d rx_buf_size %d\n", bp->dev->mtu, fp->rx_buf_size);
if (!fp->disable_tpa) {
- /* Fill the per-aggregtion pool */
+ /* Fill the per-aggregation pool */
for (i = 0; i < MAX_AGG_QS(bp); i++) {
struct bnx2x_agg_info *tpa_info =
&fp->tpa_info[i];
}
/* select a non-FCoE queue */
- return __skb_tx_hash(dev, skb, BNX2X_NUM_ETH_QUEUES(bp));
+ return __netdev_pick_tx(dev, skb) % BNX2X_NUM_ETH_QUEUES(bp);
}
void bnx2x_set_num_queues(struct bnx2x *bp)
*
* If the actual number of Tx queues (for each CoS) is less than 16 then there
* will be the holes at the end of each group of 16 ETh L2 indices (0..15,
- * 16..31,...) with indicies that are not coupled with any real Tx queue.
+ * 16..31,...) with indices that are not coupled with any real Tx queue.
*
* The proper configuration of skb->queue_mapping is handled by
* bnx2x_select_queue() and __skb_tx_hash().
ETH_OVREHEAD +
mtu +
BNX2X_FW_RX_ALIGN_END;
- /* Note : rx_buf_size doesnt take into account NET_SKB_PAD */
+ /* Note : rx_buf_size doesn't take into account NET_SKB_PAD */
if (fp->rx_buf_size + NET_SKB_PAD <= PAGE_SIZE)
fp->rx_frag_size = fp->rx_buf_size + NET_SKB_PAD;
else
int i;
u8 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp);
- /* Prepare the initial contents fo the indirection table if RSS is
+ /* Prepare the initial contents for the indirection table if RSS is
* enabled
*/
for (i = 0; i < sizeof(bp->rss_conf_obj.ind_table); i++)
/*
* Cleans the object that have internal lists without sending
- * ramrods. Should be run when interrutps are disabled.
+ * ramrods. Should be run when interrupts are disabled.
*/
void bnx2x_squeeze_objects(struct bnx2x *bp)
{
bp->fw_stats_data_mapping = bp->fw_stats_mapping +
bp->fw_stats_req_sz;
- DP(BNX2X_MSG_SP, "statistics request base address set to %x %x",
+ DP(BNX2X_MSG_SP, "statistics request base address set to %x %x\n",
U64_HI(bp->fw_stats_req_mapping),
U64_LO(bp->fw_stats_req_mapping));
- DP(BNX2X_MSG_SP, "statistics data base address set to %x %x",
+ DP(BNX2X_MSG_SP, "statistics data base address set to %x %x\n",
U64_HI(bp->fw_stats_data_mapping),
U64_LO(bp->fw_stats_data_mapping));
return 0;
/* send load request to mcp and analyze response */
static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code)
{
+ u32 param;
+
/* init fw_seq */
bp->fw_seq =
(SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
DRV_PULSE_SEQ_MASK);
BNX2X_DEV_INFO("drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq);
+ param = DRV_MSG_CODE_LOAD_REQ_WITH_LFA;
+
+ if (IS_MF_SD(bp) && bnx2x_port_after_undi(bp))
+ param |= DRV_MSG_CODE_LOAD_REQ_FORCE_LFA;
+
/* load request */
- (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ,
- DRV_MSG_CODE_LOAD_REQ_WITH_LFA);
+ (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, param);
/* if mcp fails to respond we must abort */
if (!(*load_code)) {
/* abort nic load if version mismatch */
if (my_fw != loaded_fw) {
- BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. aborting\n",
+ BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. Aborting\n",
loaded_fw, my_fw);
return -EBUSY;
}
static void bnx2x_bz_fp(struct bnx2x *bp, int index)
{
struct bnx2x_fastpath *fp = &bp->fp[index];
-
int cos;
struct napi_struct orig_napi = fp->napi;
struct bnx2x_agg_info *orig_tpa_info = fp->tpa_info;
+
/* bzero bnx2x_fastpath contents */
if (fp->tpa_info)
memset(fp->tpa_info, 0, ETH_MAX_AGGREGATION_QUEUES_E1H_E2 *
fp->txdata_ptr[cos] = &bp->bnx2x_txq[cos *
BNX2X_NUM_ETH_QUEUES(bp) + index];
- /*
- * set the tpa flag for each queue. The tpa flag determines the queue
+ /* set the tpa flag for each queue. The tpa flag determines the queue
* minimal size so it must be set prior to queue memory allocation
*/
fp->disable_tpa = !(bp->flags & TPA_ENABLE_FLAG ||
if (bp->state == BNX2X_STATE_OPEN)
bnx2x_cnic_notify(bp, CNIC_CTL_START_CMD);
-
DP(NETIF_MSG_IFUP, "Ending successfully CNIC-related load\n");
return 0;
bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD;
+ /* zero the structure w/o any lock, before SP handler is initialized */
memset(&bp->last_reported_link, 0, sizeof(bp->last_reported_link));
__set_bit(BNX2X_LINK_REPORT_LINK_DOWN,
&bp->last_reported_link.link_report_flags);
}
/* configure multi cos mappings in kernel.
- * this configuration may be overriden by a multi class queue discipline
- * or by a dcbx negotiation result.
+ * this configuration may be overridden by a multi class queue
+ * discipline or by a dcbx negotiation result.
*/
bnx2x_setup_tc(bp->dev, bp->max_cos);
/* Start the Tx */
switch (load_mode) {
case LOAD_NORMAL:
- /* Tx queue should be only reenabled */
+ /* Tx queue should be only re-enabled */
netif_tx_wake_all_queues(bp->dev);
break;
}
/* Nothing to do during unload if previous bnx2x_nic_load()
- * have not completed succesfully - all resourses are released.
+ * have not completed successfully - all resources are released.
*
* we can get here only after unsuccessful ndo_* callback, during which
* dev->IFF_UP flag is still on.
/* Send the UNLOAD_REQUEST to the MCP */
bnx2x_send_unload_req(bp, unload_mode);
- /*
- * Prevent transactions to host from the functions on the
+ /* Prevent transactions to host from the functions on the
* engine that doesn't reset global blocks in case of global
- * attention once gloabl blocks are reset and gates are opened
+ * attention once global blocks are reset and gates are opened
* (the engine which leader will perform the recovery
* last).
*/
}
/*
- * At this stage no more interrupts will arrive so we may safly clean
+ * At this stage no more interrupts will arrive so we may safely clean
* the queueable objects here in case they failed to get cleaned so far.
*/
if (IS_PF(bp))
bnx2x_set_reset_global(bp);
}
-
/* The last driver must disable a "close the gate" if there is no
* parity attention or "process kill" pending.
*/
rc |= XMIT_CSUM_TCP;
if (skb_is_gso_v6(skb)) {
- rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP | XMIT_CSUM_V6);
+ rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP);
if (rc & XMIT_CSUM_ENC)
rc |= XMIT_GSO_ENC_V6;
} else if (skb_is_gso(skb)) {
- rc |= (XMIT_GSO_V4 | XMIT_CSUM_V4 | XMIT_CSUM_TCP);
+ rc |= (XMIT_GSO_V4 | XMIT_CSUM_TCP);
if (rc & XMIT_CSUM_ENC)
rc |= XMIT_GSO_ENC_V4;
}
{
u16 hlen_w = 0;
u8 outerip_off, outerip_len = 0;
+
/* from outer IP to transport */
hlen_w = (skb_inner_transport_header(skb) -
skb_network_header(skb)) >> 1;
/* transport len */
- if (xmit_type & XMIT_CSUM_TCP)
- hlen_w += inner_tcp_hdrlen(skb) >> 1;
- else
- hlen_w += sizeof(struct udphdr) >> 1;
+ hlen_w += inner_tcp_hdrlen(skb) >> 1;
pbd2->fw_ip_hdr_to_payload_w = hlen_w;
- if (xmit_type & XMIT_CSUM_ENC_V4) {
+ /* outer IP header info */
+ if (xmit_type & XMIT_CSUM_V4) {
struct iphdr *iph = ip_hdr(skb);
pbd2->fw_ip_csum_wo_len_flags_frag =
bswab16(csum_fold((~iph->check) -
DP(NETIF_MSG_TX_QUEUED, "indices: txq %d, fp %d, txdata %d\n",
txq_index, fp_index, txdata_index); */
- /* enable this debug print to view the tranmission details
+ /* enable this debug print to view the transmission details
DP(NETIF_MSG_TX_QUEUED,
"transmitting packet cid %d fp index %d txdata_index %d tx_data ptr %p fp pointer %p\n",
txdata->cid, fp_index, txdata_index, txdata, fp); */
bnx2x_set_pbd_gso_e2(skb, &pbd_e2_parsing_data,
xmit_type);
else
- bnx2x_set_pbd_gso(skb, pbd_e1x, tx_start_bd,
- xmit_type);
+ bnx2x_set_pbd_gso(skb, pbd_e1x, first_bd, xmit_type);
}
/* Set the PBD's parsing_data field if not zero
/* setup tc must be called under rtnl lock */
ASSERT_RTNL();
- /* no traffic classes requested. aborting */
+ /* no traffic classes requested. Aborting */
if (!num_tc) {
netdev_reset_tc(dev);
return 0;
/* requested to support too many traffic classes */
if (num_tc > bp->max_cos) {
- BNX2X_ERR("support for too many traffic classes requested: %d. max supported is %d\n",
+ BNX2X_ERR("support for too many traffic classes requested: %d. Max supported is %d\n",
num_tc, bp->max_cos);
return -EINVAL;
}
prio, bp->prio_to_cos[prio]);
}
-
- /* Use this configuration to diffrentiate tc0 from other COSes
+ /* Use this configuration to differentiate tc0 from other COSes
This can be used for ets or pfc, and save the effort of setting
up a multio class queue disc or negotiating DCBX with a switch
netdev_set_prio_tc_map(dev, 0, 0);
alloc_err:
bnx2x_free_mem_bp(bp);
return -ENOMEM;
-
}
int bnx2x_reload_if_running(struct net_device *dev)
}
return sel_phy_idx;
-
}
int bnx2x_get_link_cfg_idx(struct bnx2x *bp)
{
{
struct bnx2x *bp = netdev_priv(dev);
u32 flags = bp->flags;
+ u32 changes;
bool bnx2x_reload = false;
if (features & NETIF_F_LRO)
}
}
- if (flags ^ bp->flags) {
- bp->flags = flags;
+ changes = flags ^ bp->flags;
+
+ /* if GRO is changed while LRO is enabled, don't force a reload */
+ if ((changes & GRO_ENABLE_FLAG) && (flags & TPA_ENABLE_FLAG))
+ changes &= ~GRO_ENABLE_FLAG;
+
+ if (changes)
bnx2x_reload = true;
- }
+
+ bp->flags = flags;
if (bnx2x_reload) {
if (bp->recovery_state == BNX2X_RECOVERY_DONE)
return rc;
}
-
void bnx2x_set_ctx_validation(struct bnx2x *bp, struct eth_context *cxt,
u32 cid)
{
u8 fw_sb_id, u8 sb_index,
u8 ticks)
{
-
u32 addr = BAR_CSTRORM_INTMEM +
CSTORM_STATUS_BLOCK_DATA_TIMEOUT_OFFSET(fw_sb_id, sb_index);
REG_WR8(bp, addr, ticks);
event = APE_EVENT_STATUS_STATE_UNLOAD;
break;
- case RESET_KIND_SUSPEND:
- event = APE_EVENT_STATUS_STATE_SUSPEND;
- break;
default:
return;
}
if (err)
return err;
- if (enable)
+ if (enable)
val |= MII_TG3_AUXCTL_ACTL_SMDSP_ENA;
else
val &= ~MII_TG3_AUXCTL_ACTL_SMDSP_ENA;
break;
}
}
-
- if (kind == RESET_KIND_INIT ||
- kind == RESET_KIND_SUSPEND)
- tg3_ape_driver_state_change(tp, kind);
}
/* tp->lock is held. */
break;
}
}
-
- if (kind == RESET_KIND_SHUTDOWN)
- tg3_ape_driver_state_change(tp, kind);
}
/* tp->lock is held. */
tg3_phy_toggle_auxctl_smdsp(tp, false);
}
+static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_eee *eee)
+{
+ u32 val;
+ struct ethtool_eee *dest = &tp->eee;
+
+ if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP))
+ return;
+
+ if (eee)
+ dest = eee;
+
+ if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, TG3_CL45_D7_EEERES_STAT, &val))
+ return;
+
+ /* Pull eee_active */
+ if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T ||
+ val == TG3_CL45_D7_EEERES_STAT_LP_100TX) {
+ dest->eee_active = 1;
+ } else
+ dest->eee_active = 0;
+
+ /* Pull lp advertised settings */
+ if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, &val))
+ return;
+ dest->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
+
+ /* Pull advertised and eee_enabled settings */
+ if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val))
+ return;
+ dest->eee_enabled = !!val;
+ dest->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
+
+ /* Pull tx_lpi_enabled */
+ val = tr32(TG3_CPMU_EEE_MODE);
+ dest->tx_lpi_enabled = !!(val & TG3_CPMU_EEEMD_LPI_IN_TX);
+
+ /* Pull lpi timer value */
+ dest->tx_lpi_timer = tr32(TG3_CPMU_EEE_DBTMR1) & 0xffff;
+}
+
static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up)
{
u32 val;
tw32(TG3_CPMU_EEE_CTRL, eeectl);
- tg3_phy_cl45_read(tp, MDIO_MMD_AN,
- TG3_CL45_D7_EEERES_STAT, &val);
-
- if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T ||
- val == TG3_CL45_D7_EEERES_STAT_LP_100TX)
+ tg3_eee_pull_config(tp, NULL);
+ if (tp->eee.eee_active)
tp->setlpicnt = 2;
}
tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
+ tg3_ape_driver_state_change(tp, RESET_KIND_SHUTDOWN);
+
return 0;
}
/* Advertise 1000-BaseT EEE ability */
if (advertise & ADVERTISED_1000baseT_Full)
val |= MDIO_AN_EEE_ADV_1000T;
+
+ if (!tp->eee.eee_enabled) {
+ val = 0;
+ tp->eee.advertised = 0;
+ } else {
+ tp->eee.advertised = advertise &
+ (ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full);
+ }
+
err = tg3_phy_cl45_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val);
if (err)
val = 0;
static bool tg3_phy_eee_config_ok(struct tg3 *tp)
{
- u32 val;
- u32 tgtadv = 0;
- u32 advertising = tp->link_config.advertising;
+ struct ethtool_eee eee;
if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP))
return true;
- if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val))
- return false;
-
- val &= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
-
+ tg3_eee_pull_config(tp, &eee);
- if (advertising & ADVERTISED_100baseT_Full)
- tgtadv |= MDIO_AN_EEE_ADV_100TX;
- if (advertising & ADVERTISED_1000baseT_Full)
- tgtadv |= MDIO_AN_EEE_ADV_1000T;
-
- if (val != tgtadv)
- return false;
+ if (tp->eee.eee_enabled) {
+ if (tp->eee.advertised != eee.advertised ||
+ tp->eee.tx_lpi_timer != eee.tx_lpi_timer ||
+ tp->eee.tx_lpi_enabled != eee.tx_lpi_enabled)
+ return false;
+ } else {
+ /* EEE is disabled but we're advertising */
+ if (eee.advertised)
+ return false;
+ }
return true;
}
udelay(40);
}
+static void tg3_setup_eee(struct tg3 *tp)
+{
+ u32 val;
+
+ val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 |
+ TG3_CPMU_EEE_LNKIDL_UART_IDL;
+ if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0)
+ val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT;
+
+ tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val);
+
+ tw32_f(TG3_CPMU_EEE_CTRL,
+ TG3_CPMU_EEE_CTRL_EXIT_20_1_US);
+
+ val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET |
+ (tp->eee.tx_lpi_enabled ? TG3_CPMU_EEEMD_LPI_IN_TX : 0) |
+ TG3_CPMU_EEEMD_LPI_IN_RX |
+ TG3_CPMU_EEEMD_EEE_ENABLE;
+
+ if (tg3_asic_rev(tp) != ASIC_REV_5717)
+ val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN;
+
+ if (tg3_flag(tp, ENABLE_APE))
+ val |= TG3_CPMU_EEEMD_APE_TX_DET_EN;
+
+ tw32_f(TG3_CPMU_EEE_MODE, tp->eee.eee_enabled ? val : 0);
+
+ tw32_f(TG3_CPMU_EEE_DBTMR1,
+ TG3_CPMU_DBTMR1_PCIEXIT_2047US |
+ (tp->eee.tx_lpi_timer & 0xffff));
+
+ tw32_f(TG3_CPMU_EEE_DBTMR2,
+ TG3_CPMU_DBTMR2_APE_TX_2047US |
+ TG3_CPMU_DBTMR2_TXIDXEQ_2047US);
+}
+
static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset)
{
bool current_link_up;
*/
if (!eee_config_ok &&
(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
- !force_reset)
+ !force_reset) {
+ tg3_setup_eee(tp);
tg3_phy_reset(tp);
+ }
} else {
if (!(bmcr & BMCR_ANENABLE) &&
tp->link_config.speed == current_speed &&
"Please report the problem to the driver maintainer "
"and include system chipset information.\n");
- spin_lock(&tp->lock);
tg3_flag_set(tp, TX_RECOVERY_PENDING);
- spin_unlock(&tp->lock);
}
static inline u32 tg3_tx_avail(struct tg3_napi *tnapi)
}
/* tp->lock is held. */
-static void tg3_rings_reset(struct tg3 *tp)
+static void tg3_tx_rcbs_disable(struct tg3 *tp)
{
- int i;
- u32 stblk, txrcb, rxrcb, limit;
- struct tg3_napi *tnapi = &tp->napi[0];
+ u32 txrcb, limit;
/* Disable all transmit rings but the first. */
if (!tg3_flag(tp, 5705_PLUS))
txrcb < limit; txrcb += TG3_BDINFO_SIZE)
tg3_write_mem(tp, txrcb + TG3_BDINFO_MAXLEN_FLAGS,
BDINFO_FLAGS_DISABLED);
+}
+
+/* tp->lock is held. */
+static void tg3_tx_rcbs_init(struct tg3 *tp)
+{
+ int i = 0;
+ u32 txrcb = NIC_SRAM_SEND_RCB;
+
+ if (tg3_flag(tp, ENABLE_TSS))
+ i++;
+
+ for (; i < tp->irq_max; i++, txrcb += TG3_BDINFO_SIZE) {
+ struct tg3_napi *tnapi = &tp->napi[i];
+
+ if (!tnapi->tx_ring)
+ continue;
+
+ tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
+ (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT),
+ NIC_SRAM_TX_BUFFER_DESC);
+ }
+}
+/* tp->lock is held. */
+static void tg3_rx_ret_rcbs_disable(struct tg3 *tp)
+{
+ u32 rxrcb, limit;
/* Disable all receive return rings but the first. */
if (tg3_flag(tp, 5717_PLUS))
rxrcb < limit; rxrcb += TG3_BDINFO_SIZE)
tg3_write_mem(tp, rxrcb + TG3_BDINFO_MAXLEN_FLAGS,
BDINFO_FLAGS_DISABLED);
+}
+
+/* tp->lock is held. */
+static void tg3_rx_ret_rcbs_init(struct tg3 *tp)
+{
+ int i = 0;
+ u32 rxrcb = NIC_SRAM_RCV_RET_RCB;
+
+ if (tg3_flag(tp, ENABLE_RSS))
+ i++;
+
+ for (; i < tp->irq_max; i++, rxrcb += TG3_BDINFO_SIZE) {
+ struct tg3_napi *tnapi = &tp->napi[i];
+
+ if (!tnapi->rx_rcb)
+ continue;
+
+ tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
+ (tp->rx_ret_ring_mask + 1) <<
+ BDINFO_FLAGS_MAXLEN_SHIFT, 0);
+ }
+}
+
+/* tp->lock is held. */
+static void tg3_rings_reset(struct tg3 *tp)
+{
+ int i;
+ u32 stblk;
+ struct tg3_napi *tnapi = &tp->napi[0];
+
+ tg3_tx_rcbs_disable(tp);
+
+ tg3_rx_ret_rcbs_disable(tp);
/* Disable interrupts */
tw32_mailbox_f(tp->napi[0].int_mbox, 1);
tw32_tx_mbox(mbox + i * 8, 0);
}
- txrcb = NIC_SRAM_SEND_RCB;
- rxrcb = NIC_SRAM_RCV_RET_RCB;
-
/* Clear status block in ram. */
memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW,
((u64) tnapi->status_mapping & 0xffffffff));
- if (tnapi->tx_ring) {
- tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
- (TG3_TX_RING_SIZE <<
- BDINFO_FLAGS_MAXLEN_SHIFT),
- NIC_SRAM_TX_BUFFER_DESC);
- txrcb += TG3_BDINFO_SIZE;
- }
-
- if (tnapi->rx_rcb) {
- tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
- (tp->rx_ret_ring_mask + 1) <<
- BDINFO_FLAGS_MAXLEN_SHIFT, 0);
- rxrcb += TG3_BDINFO_SIZE;
- }
-
stblk = HOSTCC_STATBLCK_RING1;
for (i = 1, tnapi++; i < tp->irq_cnt; i++, tnapi++) {
u64 mapping = (u64)tnapi->status_mapping;
tw32(stblk + TG3_64BIT_REG_HIGH, mapping >> 32);
tw32(stblk + TG3_64BIT_REG_LOW, mapping & 0xffffffff);
+ stblk += 8;
/* Clear status block in ram. */
memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
-
- if (tnapi->tx_ring) {
- tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
- (TG3_TX_RING_SIZE <<
- BDINFO_FLAGS_MAXLEN_SHIFT),
- NIC_SRAM_TX_BUFFER_DESC);
- txrcb += TG3_BDINFO_SIZE;
- }
-
- tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
- ((tp->rx_ret_ring_mask + 1) <<
- BDINFO_FLAGS_MAXLEN_SHIFT), 0);
-
- stblk += 8;
- rxrcb += TG3_BDINFO_SIZE;
}
+
+ tg3_tx_rcbs_init(tp);
+ tg3_rx_ret_rcbs_init(tp);
}
static void tg3_setup_rxbd_thresholds(struct tg3 *tp)
}
}
+ static inline u32 tg3_lso_rd_dma_workaround_bit(struct tg3 *tp)
+ {
+ if (tg3_asic_rev(tp) == ASIC_REV_5719)
+ return TG3_LSO_RD_DMA_TX_LENGTH_WA_5719;
+ else
+ return TG3_LSO_RD_DMA_TX_LENGTH_WA_5720;
+ }
+
/* tp->lock is held. */
static int tg3_reset_hw(struct tg3 *tp, bool reset_phy)
{
if (tg3_flag(tp, INIT_COMPLETE))
tg3_abort_hw(tp, 1);
- /* Enable MAC control of LPI */
- if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) {
- val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 |
- TG3_CPMU_EEE_LNKIDL_UART_IDL;
- if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0)
- val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT;
-
- tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val);
-
- tw32_f(TG3_CPMU_EEE_CTRL,
- TG3_CPMU_EEE_CTRL_EXIT_20_1_US);
-
- val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET |
- TG3_CPMU_EEEMD_LPI_IN_TX |
- TG3_CPMU_EEEMD_LPI_IN_RX |
- TG3_CPMU_EEEMD_EEE_ENABLE;
-
- if (tg3_asic_rev(tp) != ASIC_REV_5717)
- val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN;
-
- if (tg3_flag(tp, ENABLE_APE))
- val |= TG3_CPMU_EEEMD_APE_TX_DET_EN;
-
- tw32_f(TG3_CPMU_EEE_MODE, val);
-
- tw32_f(TG3_CPMU_EEE_DBTMR1,
- TG3_CPMU_DBTMR1_PCIEXIT_2047US |
- TG3_CPMU_DBTMR1_LNKIDLE_2047US);
-
- tw32_f(TG3_CPMU_EEE_DBTMR2,
- TG3_CPMU_DBTMR2_APE_TX_2047US |
- TG3_CPMU_DBTMR2_TXIDXEQ_2047US);
- }
-
if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
!(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) {
tg3_phy_pull_config(tp);
+ tg3_eee_pull_config(tp, NULL);
tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
}
+ /* Enable MAC control of LPI */
+ if (tp->phy_flags & TG3_PHYFLG_EEE_CAP)
+ tg3_setup_eee(tp);
+
if (reset_phy)
tg3_phy_reset(tp);
tw32_f(RDMAC_MODE, rdmac_mode);
udelay(40);
- if (tg3_asic_rev(tp) == ASIC_REV_5719) {
+ if (tg3_asic_rev(tp) == ASIC_REV_5719 ||
+ tg3_asic_rev(tp) == ASIC_REV_5720) {
for (i = 0; i < TG3_NUM_RDMA_CHANNELS; i++) {
if (tr32(TG3_RDMA_LENGTH + (i << 2)) > TG3_MAX_MTU(tp))
break;
}
if (i < TG3_NUM_RDMA_CHANNELS) {
val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL);
- val |= TG3_LSO_RD_DMA_TX_LENGTH_WA;
+ val |= tg3_lso_rd_dma_workaround_bit(tp);
tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val);
- tg3_flag_set(tp, 5719_RDMA_BUG);
+ tg3_flag_set(tp, 5719_5720_RDMA_BUG);
}
}
TG3_STAT_ADD32(&sp->tx_ucast_packets, MAC_TX_STATS_UCAST);
TG3_STAT_ADD32(&sp->tx_mcast_packets, MAC_TX_STATS_MCAST);
TG3_STAT_ADD32(&sp->tx_bcast_packets, MAC_TX_STATS_BCAST);
- if (unlikely(tg3_flag(tp, 5719_RDMA_BUG) &&
+ if (unlikely(tg3_flag(tp, 5719_5720_RDMA_BUG) &&
(sp->tx_ucast_packets.low + sp->tx_mcast_packets.low +
sp->tx_bcast_packets.low) > TG3_NUM_RDMA_CHANNELS)) {
u32 val;
val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL);
- val &= ~TG3_LSO_RD_DMA_TX_LENGTH_WA;
+ val &= ~tg3_lso_rd_dma_workaround_bit(tp);
tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val);
- tg3_flag_clear(tp, 5719_RDMA_BUG);
+ tg3_flag_clear(tp, 5719_5720_RDMA_BUG);
}
TG3_STAT_ADD32(&sp->rx_octets, MAC_RX_STATS_OCTETS);
*/
err = tg3_alloc_consistent(tp);
if (err)
- goto err_out1;
+ goto out_ints_fini;
tg3_napi_init(tp);
tnapi = &tp->napi[i];
free_irq(tnapi->irq_vec, tnapi);
}
- goto err_out2;
+ goto out_napi_fini;
}
}
tg3_full_lock(tp, 0);
+ if (init)
+ tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
+
err = tg3_init_hw(tp, reset_phy);
if (err) {
tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
tg3_full_unlock(tp);
if (err)
- goto err_out3;
+ goto out_free_irq;
if (test_irq && tg3_flag(tp, USING_MSI)) {
err = tg3_test_msi(tp);
tg3_free_rings(tp);
tg3_full_unlock(tp);
- goto err_out2;
+ goto out_napi_fini;
}
if (!tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, USING_MSI)) {
return 0;
-err_out3:
+out_free_irq:
for (i = tp->irq_cnt - 1; i >= 0; i--) {
struct tg3_napi *tnapi = &tp->napi[i];
free_irq(tnapi->irq_vec, tnapi);
}
-err_out2:
+out_napi_fini:
tg3_napi_disable(tp);
tg3_napi_fini(tp);
tg3_free_consistent(tp);
-err_out1:
+out_ints_fini:
tg3_ints_fini(tp);
return err;
struct tg3 *tp = netdev_priv(dev);
bool doextlpbk = etest->flags & ETH_TEST_FL_EXTERNAL_LB;
- if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) &&
- tg3_power_up(tp)) {
- etest->flags |= ETH_TEST_FL_FAILED;
- memset(data, 1, sizeof(u64) * TG3_NUM_TEST);
- return;
+ if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
+ if (tg3_power_up(tp)) {
+ etest->flags |= ETH_TEST_FL_FAILED;
+ memset(data, 1, sizeof(u64) * TG3_NUM_TEST);
+ return;
+ }
+ tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
}
memset(data, 0, sizeof(u64) * TG3_NUM_TEST);
return 0;
}
+static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata)
+{
+ struct tg3 *tp = netdev_priv(dev);
+
+ if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) {
+ netdev_warn(tp->dev, "Board does not support EEE!\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (edata->advertised != tp->eee.advertised) {
+ netdev_warn(tp->dev,
+ "Direct manipulation of EEE advertisement is not supported\n");
+ return -EINVAL;
+ }
+
+ if (edata->tx_lpi_timer > TG3_CPMU_DBTMR1_LNKIDLE_MAX) {
+ netdev_warn(tp->dev,
+ "Maximal Tx Lpi timer supported is %#x(u)\n",
+ TG3_CPMU_DBTMR1_LNKIDLE_MAX);
+ return -EINVAL;
+ }
+
+ tp->eee = *edata;
+
+ tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
+ tg3_warn_mgmt_link_flap(tp);
+
+ if (netif_running(tp->dev)) {
+ tg3_full_lock(tp, 0);
+ tg3_setup_eee(tp);
+ tg3_phy_reset(tp);
+ tg3_full_unlock(tp);
+ }
+
+ return 0;
+}
+
+static int tg3_get_eee(struct net_device *dev, struct ethtool_eee *edata)
+{
+ struct tg3 *tp = netdev_priv(dev);
+
+ if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) {
+ netdev_warn(tp->dev,
+ "Board does not support EEE!\n");
+ return -EOPNOTSUPP;
+ }
+
+ *edata = tp->eee;
+ return 0;
+}
+
static const struct ethtool_ops tg3_ethtool_ops = {
.get_settings = tg3_get_settings,
.set_settings = tg3_set_settings,
.get_channels = tg3_get_channels,
.set_channels = tg3_set_channels,
.get_ts_info = tg3_get_ts_info,
+ .get_eee = tg3_get_eee,
+ .set_eee = tg3_set_eee,
};
static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
(tg3_asic_rev(tp) == ASIC_REV_5717 &&
tg3_chip_rev_id(tp) != CHIPREV_ID_5717_A0) ||
(tg3_asic_rev(tp) == ASIC_REV_57765 &&
- tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0)))
+ tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) {
tp->phy_flags |= TG3_PHYFLG_EEE_CAP;
+ tp->eee.supported = SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full;
+ tp->eee.advertised = ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full;
+ tp->eee.eee_enabled = 1;
+ tp->eee.tx_lpi_enabled = 1;
+ tp->eee.tx_lpi_timer = TG3_CPMU_DBTMR1_LNKIDLE_2047US;
+ }
+
tg3_phy_init_link_config(tp);
if (!(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
{
struct net_device *dev;
struct tg3 *tp;
- int i, err, pm_cap;
+ int i, err;
u32 sndmbx, rcvmbx, intmbx;
char str[40];
u64 dma_mask, persist_dma_mask;
pci_set_master(pdev);
- /* Find power-management capability. */
- pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pm_cap == 0) {
- dev_err(&pdev->dev,
- "Cannot find Power Management capability, aborting\n");
- err = -EIO;
- goto err_out_free_res;
- }
-
- err = pci_set_power_state(pdev, PCI_D0);
- if (err) {
- dev_err(&pdev->dev, "Transition to D0 failed, aborting\n");
- goto err_out_free_res;
- }
-
dev = alloc_etherdev_mq(sizeof(*tp), TG3_IRQ_MAX_VECS);
if (!dev) {
err = -ENOMEM;
- goto err_out_power_down;
+ goto err_out_free_res;
}
SET_NETDEV_DEV(dev, &pdev->dev);
tp = netdev_priv(dev);
tp->pdev = pdev;
tp->dev = dev;
- tp->pm_cap = pm_cap;
+ tp->pm_cap = pdev->pm_cap;
tp->rx_mode = TG3_DEF_RX_MODE;
tp->tx_mode = TG3_DEF_TX_MODE;
tp->irq_sync = 1;
err_out_free_dev:
free_netdev(dev);
-err_out_power_down:
- pci_set_power_state(pdev, PCI_D3hot);
-
err_out_free_res:
pci_release_regions(pdev);
tg3_full_lock(tp, 0);
+ tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
+
tg3_flag_set(tp, INIT_COMPLETE);
err = tg3_restart_hw(tp,
!(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN));
goto done;
tg3_full_lock(tp, 0);
+ tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
tg3_flag_set(tp, INIT_COMPLETE);
err = tg3_restart_hw(tp, true);
if (err) {
.driver.pm = &tg3_pm_ops,
};
-static int __init tg3_init(void)
-{
- return pci_register_driver(&tg3_driver);
-}
-
-static void __exit tg3_cleanup(void)
-{
- pci_unregister_driver(&tg3_driver);
-}
-
-module_init(tg3_init);
-module_exit(tg3_cleanup);
+module_pci_driver(tg3_driver);
#define TG3_CPMU_EEE_DBTMR1 0x000036b4
#define TG3_CPMU_DBTMR1_PCIEXIT_2047US 0x07ff0000
#define TG3_CPMU_DBTMR1_LNKIDLE_2047US 0x000007ff
+#define TG3_CPMU_DBTMR1_LNKIDLE_MAX 0x0000ffff
#define TG3_CPMU_EEE_DBTMR2 0x000036b8
#define TG3_CPMU_DBTMR2_APE_TX_2047US 0x07ff0000
#define TG3_CPMU_DBTMR2_TXIDXEQ_2047US 0x000007ff
#define TG3_LSO_RD_DMA_CRPTEN_CTRL 0x00004910
#define TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_BD_4K 0x00030000
#define TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_LSO_4K 0x000c0000
- #define TG3_LSO_RD_DMA_TX_LENGTH_WA 0x02000000
+ #define TG3_LSO_RD_DMA_TX_LENGTH_WA_5719 0x02000000
+ #define TG3_LSO_RD_DMA_TX_LENGTH_WA_5720 0x00200000
/* 0x4914 --> 0x4be0 unused */
#define TG3_NUM_RDMA_CHANNELS 4
TG3_FLAG_APE_HAS_NCSI,
TG3_FLAG_TX_TSTAMP_EN,
TG3_FLAG_4K_FIFO_LIMIT,
- TG3_FLAG_5719_RDMA_BUG,
+ TG3_FLAG_5719_5720_RDMA_BUG,
TG3_FLAG_RESET_TASK_PENDING,
TG3_FLAG_PTP_CAPABLE,
TG3_FLAG_5705_PLUS,
unsigned int irq_cnt;
struct ethtool_coalesce coal;
+ struct ethtool_eee eee;
/* firmware info */
const char *fw_needed;
u8 ipv6;
u8 vtm;
u8 pkt_type;
+ u8 ip_frag;
};
struct be_rx_obj {
#define BE_VF_UC_PMAC_COUNT 2
#define BE_FLAGS_QNQ_ASYNC_EVT_RCVD (1 << 11)
+/* Ethtool set_dump flags */
+#define LANCER_INITIATE_FW_DUMP 0x1
+
struct phy_info {
u8 transceiver;
u8 autoneg;
u32 cmd_privileges;
/* Ethtool knobs and info */
char fw_ver[FW_VER_LEN];
+ char fw_on_flash[FW_VER_LEN];
int if_handle; /* Used to configure filtering */
u32 *pmac_id; /* MAC addr handle used by BE card */
u32 beacon_state; /* for set_phys_id */
resource_error = lancer_provisioning_error(adapter);
if (resource_error)
- return -1;
+ return -EAGAIN;
status = lancer_wait_ready(adapter);
if (!status) {
* when PF provisions resources.
*/
resource_error = lancer_provisioning_error(adapter);
- if (status == -1 && !resource_error)
- adapter->eeh_error = true;
+ if (resource_error)
+ status = -EAGAIN;
return status;
}
return status;
}
+static int lancer_wait_idle(struct be_adapter *adapter)
+{
+#define SLIPORT_IDLE_TIMEOUT 30
+ u32 reg_val;
+ int status = 0, i;
+
+ for (i = 0; i < SLIPORT_IDLE_TIMEOUT; i++) {
+ reg_val = ioread32(adapter->db + PHYSDEV_CONTROL_OFFSET);
+ if ((reg_val & PHYSDEV_CONTROL_INP_MASK) == 0)
+ break;
+
+ ssleep(1);
+ }
+
+ if (i == SLIPORT_IDLE_TIMEOUT)
+ status = -1;
+
+ return status;
+}
+
+int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask)
+{
+ int status = 0;
+
+ status = lancer_wait_idle(adapter);
+ if (status)
+ return status;
+
+ iowrite32(mask, adapter->db + PHYSDEV_CONTROL_OFFSET);
+
+ return status;
+}
+
+/* Routine to check whether dump image is present or not */
+bool dump_present(struct be_adapter *adapter)
+{
+ u32 sliport_status = 0;
+
+ sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
+ return !!(sliport_status & SLIPORT_STATUS_DIP_MASK);
+}
+
+int lancer_initiate_dump(struct be_adapter *adapter)
+{
+ int status;
+
+ /* give firmware reset and diagnostic dump */
+ status = lancer_physdev_ctrl(adapter, PHYSDEV_CONTROL_FW_RESET_MASK |
+ PHYSDEV_CONTROL_DD_MASK);
+ if (status < 0) {
+ dev_err(&adapter->pdev->dev, "Firmware reset failed\n");
+ return status;
+ }
+
+ status = lancer_wait_idle(adapter);
+ if (status)
+ return status;
+
+ if (!dump_present(adapter)) {
+ dev_err(&adapter->pdev->dev, "Dump image not present\n");
+ return -1;
+ }
+
+ return 0;
+}
+
/* Uses sync mcc */
int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain)
{
#define PHYSDEV_CONTROL_OFFSET 0x414
#define SLIPORT_STATUS_ERR_MASK 0x80000000
+#define SLIPORT_STATUS_DIP_MASK 0x02000000
#define SLIPORT_STATUS_RN_MASK 0x01000000
#define SLIPORT_STATUS_RDY_MASK 0x00800000
#define SLI_PORT_CONTROL_IP_MASK 0x08000000
#define PHYSDEV_CONTROL_FW_RESET_MASK 0x00000002
+#define PHYSDEV_CONTROL_DD_MASK 0x00000004
#define PHYSDEV_CONTROL_INP_MASK 0x40000000
#define SLIPORT_ERROR_NO_RESOURCE1 0x2
u8 ip_version; /* dword 1 */
u8 macdst[6]; /* dword 1 */
u8 vtp; /* dword 1 */
- u8 rsvd0; /* dword 1 */
+ u8 ip_frag; /* dword 1 */
u8 fragndx[10]; /* dword 1 */
u8 ct[2]; /* dword 1 */
u8 sw; /* dword 1 */
return vlan_tx_tag_present(skb) || adapter->pvid || adapter->qnq_vid;
}
-static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb)
+static int be_ipv6_tx_stall_chk(struct be_adapter *adapter,
+ struct sk_buff *skb)
{
- return BE3_chip(adapter) &&
- be_ipv6_exthdr_check(skb);
+ return BE3_chip(adapter) && be_ipv6_exthdr_check(skb);
}
-static netdev_tx_t be_xmit(struct sk_buff *skb,
- struct net_device *netdev)
+static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter,
+ struct sk_buff *skb,
+ bool *skip_hw_vlan)
{
- struct be_adapter *adapter = netdev_priv(netdev);
- struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)];
- struct be_queue_info *txq = &txo->q;
- struct iphdr *ip = NULL;
- u32 wrb_cnt = 0, copied = 0;
- u32 start = txq->head, eth_hdr_len;
- bool dummy_wrb, stopped = false;
- bool skip_hw_vlan = false;
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
+ unsigned int eth_hdr_len;
+ struct iphdr *ip;
- eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ?
- VLAN_ETH_HLEN : ETH_HLEN;
+ /* Lancer ASIC has a bug wherein packets that are 32 bytes or less
+ * may cause a transmit stall on that port. So the work-around is to
+ * pad such packets to a 36-byte length.
+ */
+ if (unlikely(lancer_chip(adapter) && skb->len <= 32)) {
+ if (skb_padto(skb, 36))
+ goto tx_drop;
+ skb->len = 36;
+ }
/* For padded packets, BE HW modifies tot_len field in IP header
* incorrecly when VLAN tag is inserted by HW.
+ * For padded packets, Lancer computes incorrect checksum.
*/
- if (skb->len <= 60 && vlan_tx_tag_present(skb) && is_ipv4_pkt(skb)) {
+ eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ?
+ VLAN_ETH_HLEN : ETH_HLEN;
+ if (skb->len <= 60 &&
+ (lancer_chip(adapter) || vlan_tx_tag_present(skb)) &&
+ is_ipv4_pkt(skb)) {
ip = (struct iphdr *)ip_hdr(skb);
pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len));
}
*/
if ((adapter->function_mode & UMC_ENABLED) &&
veh->h_vlan_proto == htons(ETH_P_8021Q))
- skip_hw_vlan = true;
+ *skip_hw_vlan = true;
/* HW has a bug wherein it will calculate CSUM for VLAN
* pkts even though it is disabled.
* Manually insert VLAN in pkt.
*/
if (skb->ip_summed != CHECKSUM_PARTIAL &&
- vlan_tx_tag_present(skb)) {
- skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan);
+ vlan_tx_tag_present(skb)) {
+ skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan);
if (unlikely(!skb))
goto tx_drop;
}
* skip HW tagging is not enabled by FW.
*/
if (unlikely(be_ipv6_tx_stall_chk(adapter, skb) &&
- (adapter->pvid || adapter->qnq_vid) &&
- !qnq_async_evt_rcvd(adapter)))
+ (adapter->pvid || adapter->qnq_vid) &&
+ !qnq_async_evt_rcvd(adapter)))
goto tx_drop;
/* Manual VLAN tag insertion to prevent:
*/
if (be_ipv6_tx_stall_chk(adapter, skb) &&
be_vlan_tag_tx_chk(adapter, skb)) {
- skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan);
+ skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan);
if (unlikely(!skb))
goto tx_drop;
}
+ return skb;
+tx_drop:
+ dev_kfree_skb_any(skb);
+ return NULL;
+}
+
+static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)];
+ struct be_queue_info *txq = &txo->q;
+ bool dummy_wrb, stopped = false;
+ u32 wrb_cnt = 0, copied = 0;
+ bool skip_hw_vlan = false;
+ u32 start = txq->head;
+
+ skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan);
+ if (!skb)
+ return NETDEV_TX_OK;
+
wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb);
copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb,
txq->head = start;
dev_kfree_skb_any(skb);
}
-tx_drop:
return NETDEV_TX_OK;
}
compl);
}
rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, port, compl);
+ rxcp->ip_frag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0,
+ ip_frag, compl);
}
static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
else
be_parse_rx_compl_v0(compl, rxcp);
+ if (rxcp->ip_frag)
+ rxcp->l4_csum = 0;
+
if (rxcp->vlanf) {
/* vlanf could be wrongly set in some cards.
* ignore if vtm is not set */
static inline bool do_gro(struct be_rx_compl_info *rxcp)
{
- return (rxcp->tcpf && !rxcp->err) ? true : false;
+ return (rxcp->tcpf && !rxcp->err && rxcp->l4_csum) ? true : false;
}
static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi,
if (status)
goto err;
- be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL);
+ be_cmd_get_fw_ver(adapter, adapter->fw_ver, adapter->fw_on_flash);
if (adapter->vlans_added)
be_vid_config(adapter);
return 0;
}
-static int lancer_wait_idle(struct be_adapter *adapter)
-{
-#define SLIPORT_IDLE_TIMEOUT 30
- u32 reg_val;
- int status = 0, i;
-
- for (i = 0; i < SLIPORT_IDLE_TIMEOUT; i++) {
- reg_val = ioread32(adapter->db + PHYSDEV_CONTROL_OFFSET);
- if ((reg_val & PHYSDEV_CONTROL_INP_MASK) == 0)
- break;
-
- ssleep(1);
- }
-
- if (i == SLIPORT_IDLE_TIMEOUT)
- status = -1;
-
- return status;
-}
-
-static int lancer_fw_reset(struct be_adapter *adapter)
-{
- int status = 0;
-
- status = lancer_wait_idle(adapter);
- if (status)
- return status;
-
- iowrite32(PHYSDEV_CONTROL_FW_RESET_MASK, adapter->db +
- PHYSDEV_CONTROL_OFFSET);
-
- return status;
-}
-
static int lancer_fw_download(struct be_adapter *adapter,
const struct firmware *fw)
{
}
if (change_status == LANCER_FW_RESET_NEEDED) {
- status = lancer_fw_reset(adapter);
+ status = lancer_physdev_ctrl(adapter,
+ PHYSDEV_CONTROL_FW_RESET_MASK);
if (status) {
dev_err(&adapter->pdev->dev,
"Adapter busy for FW reset.\n"
else
status = be_fw_download(adapter, fw);
+ if (!status)
+ be_cmd_get_fw_ver(adapter, adapter->fw_ver,
+ adapter->fw_on_flash);
+
fw_exit:
release_firmware(fw);
return status;
static int lancer_recover_func(struct be_adapter *adapter)
{
+ struct device *dev = &adapter->pdev->dev;
int status;
status = lancer_test_and_set_rdy_state(adapter);
be_clear(adapter);
- adapter->hw_error = false;
- adapter->fw_timeout = false;
+ be_clear_all_error(adapter);
status = be_setup(adapter);
if (status)
goto err;
}
- dev_err(&adapter->pdev->dev,
- "Adapter SLIPORT recovery succeeded\n");
+ dev_err(dev, "Error recovery successful\n");
return 0;
err:
- if (adapter->eeh_error)
- dev_err(&adapter->pdev->dev,
- "Adapter SLIPORT recovery failed\n");
+ if (status == -EAGAIN)
+ dev_err(dev, "Waiting for resource provisioning\n");
+ else
+ dev_err(dev, "Error recovery failed\n");
return status;
}
{
struct be_adapter *adapter =
container_of(work, struct be_adapter, func_recovery_work.work);
- int status;
+ int status = 0;
be_detect_error(adapter);
if (adapter->hw_error && lancer_chip(adapter)) {
- if (adapter->eeh_error)
- goto out;
-
rtnl_lock();
netif_device_detach(adapter->netdev);
rtnl_unlock();
status = lancer_recover_func(adapter);
-
if (!status)
netif_device_attach(adapter->netdev);
}
- out:
- schedule_delayed_work(&adapter->func_recovery_work,
- msecs_to_jiffies(1000));
+ /* In Lancer, for all errors other than provisioning error (-EAGAIN),
+ * no need to attempt further recovery.
+ */
+ if (!status || status == -EAGAIN)
+ schedule_delayed_work(&adapter->func_recovery_work,
+ msecs_to_jiffies(1000));
}
static void be_worker(struct work_struct *work)
dev_err(&adapter->pdev->dev, "EEH error detected\n");
- adapter->eeh_error = true;
-
- cancel_delayed_work_sync(&adapter->func_recovery_work);
+ if (!adapter->eeh_error) {
+ adapter->eeh_error = true;
- rtnl_lock();
- netif_device_detach(netdev);
- rtnl_unlock();
+ cancel_delayed_work_sync(&adapter->func_recovery_work);
- if (netif_running(netdev)) {
rtnl_lock();
- be_close(netdev);
+ netif_device_detach(netdev);
+ if (netif_running(netdev))
+ be_close(netdev);
rtnl_unlock();
+
+ be_clear(adapter);
}
- be_clear(adapter);
if (state == pci_channel_io_perm_failure)
return PCI_ERS_RESULT_DISCONNECT;
int status;
dev_info(&adapter->pdev->dev, "EEH reset\n");
- be_clear_all_error(adapter);
status = pci_enable_device(pdev);
if (status)
return PCI_ERS_RESULT_DISCONNECT;
pci_cleanup_aer_uncorrect_error_status(pdev);
+ be_clear_all_error(adapter);
return PCI_ERS_RESULT_RECOVERED;
}
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_net.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <asm/cacheflush.h>
int i;
unsigned int *buf = bufaddr;
- for (i = 0; i < (len + 3) / 4; i++, buf++)
+ for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++)
*buf = cpu_to_be32(*buf);
return bufaddr;
iap = &tmpaddr[0];
}
+ /*
+ * 5) random mac address
+ */
+ if (!is_valid_ether_addr(iap)) {
+ /* Report it and use a random ethernet address instead */
+ netdev_err(ndev, "Invalid MAC address: %pM\n", iap);
+ eth_hw_addr_random(ndev);
+ netdev_info(ndev, "Using random MAC address: %pM\n",
+ ndev->dev_addr);
+ return;
+ }
+
memcpy(ndev->dev_addr, iap, ETH_ALEN);
/* Adjust MAC if using macaddr */
struct resource *r;
const struct of_device_id *of_id;
static int dev_id;
- struct pinctrl *pinctrl;
- struct regulator *reg_phy;
of_id = of_match_device(fec_dt_ids, &pdev->dev);
if (of_id)
fep->phy_interface = ret;
}
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto failed_pin;
- }
-
fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(fep->clk_ipg)) {
ret = PTR_ERR(fep->clk_ipg);
clk_prepare_enable(fep->clk_enet_out);
clk_prepare_enable(fep->clk_ptp);
- reg_phy = devm_regulator_get(&pdev->dev, "phy");
- if (!IS_ERR(reg_phy)) {
- ret = regulator_enable(reg_phy);
+ fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");
+ if (!IS_ERR(fep->reg_phy)) {
+ ret = regulator_enable(fep->reg_phy);
if (ret) {
dev_err(&pdev->dev,
"Failed to enable phy regulator: %d\n", ret);
goto failed_regulator;
}
+ } else {
+ fep->reg_phy = NULL;
}
fec_reset_phy(pdev);
failed_register:
fec_enet_mii_remove(fep);
failed_mii_init:
-failed_init:
+failed_irq:
for (i = 0; i < FEC_IRQ_NUM; i++) {
irq = platform_get_irq(pdev, i);
if (irq > 0)
free_irq(irq, ndev);
}
-failed_irq:
+failed_init:
+ if (fep->reg_phy)
+ regulator_disable(fep->reg_phy);
failed_regulator:
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
clk_disable_unprepare(fep->clk_enet_out);
clk_disable_unprepare(fep->clk_ptp);
-failed_pin:
failed_clk:
failed_ioremap:
free_netdev(ndev);
unregister_netdev(ndev);
fec_enet_mii_remove(fep);
del_timer_sync(&fep->time_keep);
+ for (i = 0; i < FEC_IRQ_NUM; i++) {
+ int irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ free_irq(irq, ndev);
+ }
+ if (fep->reg_phy)
+ regulator_disable(fep->reg_phy);
clk_disable_unprepare(fep->clk_ptp);
if (fep->ptp_clock)
ptp_clock_unregister(fep->ptp_clock);
clk_disable_unprepare(fep->clk_enet_out);
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
- for (i = 0; i < FEC_IRQ_NUM; i++) {
- int irq = platform_get_irq(pdev, i);
- if (irq > 0)
- free_irq(irq, ndev);
- }
free_netdev(ndev);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
+ if (fep->reg_phy)
+ regulator_disable(fep->reg_phy);
+
return 0;
}
{
struct net_device *ndev = dev_get_drvdata(dev);
struct fec_enet_private *fep = netdev_priv(ndev);
+ int ret;
+
+ if (fep->reg_phy) {
+ ret = regulator_enable(fep->reg_phy);
+ if (ret)
+ return ret;
+ }
clk_prepare_enable(fep->clk_enet_out);
clk_prepare_enable(fep->clk_ahb);
dev_err(&pdev->dev, "net device registration failed.\n");
ql_release_all(pdev);
pci_disable_device(pdev);
+ free_netdev(ndev);
return err;
}
/* Start up the timer to trigger EEH if
.err_handler = &qlge_err_handler
};
-static int __init qlge_init_module(void)
-{
- return pci_register_driver(&qlge_driver);
-}
-
-static void __exit qlge_exit(void)
-{
- pci_unregister_driver(&qlge_driver);
-}
-
-module_init(qlge_init_module);
-module_exit(qlge_exit);
+module_pci_driver(qlge_driver);
* Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device.
*
* This is a new flat driver which is based on the original emac_lite
- * driver from John Williams <john.williams@petalogix.com>.
+ * driver from John Williams <john.williams@xilinx.com>.
*
- * 2007-2009 (c) Xilinx, Inc.
+ * 2007 - 2013 (c) Xilinx, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
u32 reg_data;
/* Enable the Tx interrupts for the first Buffer */
- reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
- out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
- reg_data | XEL_TSR_XMIT_IE_MASK);
+ reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+ __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
+ drvdata->base_addr + XEL_TSR_OFFSET);
/* Enable the Tx interrupts for the second Buffer if
* configured in HW */
if (drvdata->tx_ping_pong != 0) {
- reg_data = in_be32(drvdata->base_addr +
+ reg_data = __raw_readl(drvdata->base_addr +
XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
- out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
- XEL_TSR_OFFSET,
- reg_data | XEL_TSR_XMIT_IE_MASK);
+ __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
+ drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
}
/* Enable the Rx interrupts for the first buffer */
- out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
- XEL_RSR_RECV_IE_MASK);
+ __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
/* Enable the Rx interrupts for the second Buffer if
* configured in HW */
if (drvdata->rx_ping_pong != 0) {
- out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
- XEL_RSR_OFFSET,
- XEL_RSR_RECV_IE_MASK);
+ __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr +
+ XEL_BUFFER_OFFSET + XEL_RSR_OFFSET);
}
/* Enable the Global Interrupt Enable */
- out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+ __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
}
/**
u32 reg_data;
/* Disable the Global Interrupt Enable */
- out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+ __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
/* Disable the Tx interrupts for the first buffer */
- reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
- out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
- reg_data & (~XEL_TSR_XMIT_IE_MASK));
+ reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+ __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
+ drvdata->base_addr + XEL_TSR_OFFSET);
/* Disable the Tx interrupts for the second Buffer
* if configured in HW */
if (drvdata->tx_ping_pong != 0) {
- reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET +
XEL_TSR_OFFSET);
- out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
- XEL_TSR_OFFSET,
- reg_data & (~XEL_TSR_XMIT_IE_MASK));
+ __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
+ drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
}
/* Disable the Rx interrupts for the first buffer */
- reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET);
- out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
- reg_data & (~XEL_RSR_RECV_IE_MASK));
+ reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET);
+ __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
+ drvdata->base_addr + XEL_RSR_OFFSET);
/* Disable the Rx interrupts for the second buffer
* if configured in HW */
if (drvdata->rx_ping_pong != 0) {
- reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+ reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET +
XEL_RSR_OFFSET);
- out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
- XEL_RSR_OFFSET,
- reg_data & (~XEL_RSR_RECV_IE_MASK));
+ __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
+ drvdata->base_addr + XEL_BUFFER_OFFSET +
+ XEL_RSR_OFFSET);
}
}
byte_count = ETH_FRAME_LEN;
/* Check if the expected buffer is available */
- reg_data = in_be32(addr + XEL_TSR_OFFSET);
+ reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
addr = (void __iomem __force *)((u32 __force)addr ^
XEL_BUFFER_OFFSET);
- reg_data = in_be32(addr + XEL_TSR_OFFSET);
+ reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
/* Write the frame to the buffer */
xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
- out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK));
+ __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK),
+ addr + XEL_TPLR_OFFSET);
/* Update the Tx Status Register to indicate that there is a
* frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
* is used by the interrupt handler to check whether a frame
* has been transmitted */
- reg_data = in_be32(addr + XEL_TSR_OFFSET);
+ reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
- out_be32(addr + XEL_TSR_OFFSET, reg_data);
+ __raw_writel(reg_data, addr + XEL_TSR_OFFSET);
return 0;
}
addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
/* Verify which buffer has valid data */
- reg_data = in_be32(addr + XEL_RSR_OFFSET);
+ reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
if (drvdata->rx_ping_pong != 0)
return 0; /* No data was available */
/* Verify that buffer has valid data */
- reg_data = in_be32(addr + XEL_RSR_OFFSET);
+ reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
XEL_RSR_RECV_DONE_MASK)
return 0; /* No data was available */
}
/* Get the protocol type of the ethernet frame that arrived */
- proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET +
+ proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
if (proto_type == ETH_P_IP) {
- length = ((ntohl(in_be32(addr +
+ length = ((ntohl(__raw_readl(addr +
XEL_HEADER_IP_LENGTH_OFFSET +
XEL_RXBUFF_OFFSET)) >>
XEL_HEADER_SHIFT) &
data, length);
/* Acknowledge the frame */
- reg_data = in_be32(addr + XEL_RSR_OFFSET);
+ reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
reg_data &= ~XEL_RSR_RECV_DONE_MASK;
- out_be32(addr + XEL_RSR_OFFSET, reg_data);
+ __raw_writel(reg_data, addr + XEL_RSR_OFFSET);
return length;
}
xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
- out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN);
+ __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
/* Update the MAC address in the EmacLite */
- reg_data = in_be32(addr + XEL_TSR_OFFSET);
- out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR);
+ reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
/* Wait for EmacLite to finish with the MAC address update */
- while ((in_be32(addr + XEL_TSR_OFFSET) &
+ while ((__raw_readl(addr + XEL_TSR_OFFSET) &
XEL_TSR_PROG_MAC_ADDR) != 0)
;
}
u32 tx_status;
/* Check if there is Rx Data available */
- if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) ||
- (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
+ if ((__raw_readl(base_addr + XEL_RSR_OFFSET) &
+ XEL_RSR_RECV_DONE_MASK) ||
+ (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
& XEL_RSR_RECV_DONE_MASK))
xemaclite_rx_handler(dev);
/* Check if the Transmission for the first buffer is completed */
- tx_status = in_be32(base_addr + XEL_TSR_OFFSET);
+ tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
- out_be32(base_addr + XEL_TSR_OFFSET, tx_status);
+ __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET);
tx_complete = true;
}
/* Check if the Transmission for the second buffer is completed */
- tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
- out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET,
- tx_status);
+ __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
tx_complete = true;
}
/* wait for the MDIO interface to not be busy or timeout
after some time.
*/
- while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
+ while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
XEL_MDIOCTRL_MDIOSTS_MASK) {
if (end - jiffies <= 0) {
WARN_ON(1);
* MDIO Address register. Set the Status bit in the MDIO Control
* register to start a MDIO read transaction.
*/
- ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET);
- out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET,
- XEL_MDIOADDR_OP_MASK |
- ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg));
- out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET,
- ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK);
+ ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ __raw_writel(XEL_MDIOADDR_OP_MASK |
+ ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+ lp->base_addr + XEL_MDIOADDR_OFFSET);
+ __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
if (xemaclite_mdio_wait(lp))
return -ETIMEDOUT;
- rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET);
+ rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET);
dev_dbg(&lp->ndev->dev,
"xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n",
* Data register. Finally, set the Status bit in the MDIO Control
* register to start a MDIO write transaction.
*/
- ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET);
- out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET,
- ~XEL_MDIOADDR_OP_MASK &
- ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg));
- out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val);
- out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET,
- ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK);
+ ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ __raw_writel(~XEL_MDIOADDR_OP_MASK &
+ ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+ lp->base_addr + XEL_MDIOADDR_OFFSET);
+ __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
+ __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
return 0;
}
int rc;
struct resource res;
struct device_node *np = of_get_parent(lp->phy_node);
+ struct device_node *npp;
/* Don't register the MDIO bus if the phy_node or its parent node
* can't be found.
*/
- if (!np)
+ if (!np) {
+ dev_err(dev, "Failed to register mdio bus.\n");
return -ENODEV;
+ }
+ npp = of_get_parent(np);
+
+ of_address_to_resource(npp, 0, &res);
+ if (lp->ndev->mem_start != res.start) {
+ struct phy_device *phydev;
+ phydev = of_phy_find_device(lp->phy_node);
+ if (!phydev)
+ dev_info(dev,
+ "MDIO of the phy is not registered yet\n");
+ return 0;
+ }
/* Enable the MDIO bus by asserting the enable bit in MDIO Control
* register.
*/
- out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET,
- XEL_MDIOCTRL_MDIOEN_MASK);
+ __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
bus = mdiobus_alloc();
- if (!bus)
+ if (!bus) {
+ dev_err(dev, "Failed to allocate mdiobus\n");
return -ENOMEM;
+ }
- of_address_to_resource(np, 0, &res);
snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
(unsigned long long)res.start);
bus->priv = lp;
lp->mii_bus = bus;
rc = of_mdiobus_register(bus, np);
- if (rc)
+ if (rc) {
+ dev_err(dev, "Failed to register mdio bus.\n");
goto err_register;
+ }
return 0;
* There's nothing in the Emaclite device to be configured when the link
* state changes. We just print the status.
*/
-void xemaclite_adjust_link(struct net_device *ndev)
+static void xemaclite_adjust_link(struct net_device *ndev)
{
struct net_local *lp = netdev_priv(ndev);
struct phy_device *phy = lp->phy_dev;
phy_write(lp->phy_dev, MII_CTRL1000, 0);
/* Advertise only 10 and 100mbps full/half duplex speeds */
- phy_write(lp->phy_dev, MII_ADVERTISE, ADVERTISE_ALL);
+ phy_write(lp->phy_dev, MII_ADVERTISE, ADVERTISE_ALL |
+ ADVERTISE_CSMA);
/* Restart auto negotiation */
bmcr = phy_read(lp->phy_dev, MII_BMCR);
* This function un maps the IO region of the Emaclite device and frees the net
* device.
*/
-static void xemaclite_remove_ndev(struct net_device *ndev)
+static void xemaclite_remove_ndev(struct net_device *ndev,
+ struct platform_device *pdev)
{
if (ndev) {
struct net_local *lp = netdev_priv(ndev);
if (lp->base_addr)
- iounmap((void __iomem __force *) (lp->base_addr));
+ devm_iounmap(&pdev->dev, lp->base_addr);
free_netdev(ndev);
}
}
*/
static int xemaclite_of_probe(struct platform_device *ofdev)
{
- struct resource r_irq; /* Interrupt resources */
- struct resource r_mem; /* IO mem resources */
+ struct resource *res;
struct net_device *ndev = NULL;
struct net_local *lp = NULL;
struct device *dev = &ofdev->dev;
dev_info(dev, "Device Tree Probing\n");
- /* Get iospace for the device */
- rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem);
- if (rc) {
- dev_err(dev, "invalid address\n");
- return rc;
- }
-
- /* Get IRQ for the device */
- rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq);
- if (!rc) {
- dev_err(dev, "no IRQ found\n");
- return rc;
- }
-
/* Create an ethernet device instance */
ndev = alloc_etherdev(sizeof(struct net_local));
if (!ndev)
dev_set_drvdata(dev, ndev);
SET_NETDEV_DEV(ndev, &ofdev->dev);
- ndev->irq = r_irq.start;
- ndev->mem_start = r_mem.start;
- ndev->mem_end = r_mem.end;
-
lp = netdev_priv(ndev);
lp->ndev = ndev;
- if (!request_mem_region(ndev->mem_start,
- ndev->mem_end - ndev->mem_start + 1,
- DRIVER_NAME)) {
- dev_err(dev, "Couldn't lock memory region at %p\n",
- (void *)ndev->mem_start);
- rc = -EBUSY;
- goto error2;
+ /* Get IRQ for the device */
+ res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no IRQ found\n");
+ goto error;
}
- /* Get the virtual base address for the device */
- lp->base_addr = ioremap(r_mem.start, resource_size(&r_mem));
- if (NULL == lp->base_addr) {
- dev_err(dev, "EmacLite: Could not allocate iomem\n");
- rc = -EIO;
- goto error1;
- }
+ ndev->irq = res->start;
+
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ lp->base_addr = devm_request_and_ioremap(&ofdev->dev, res);
+ if (!lp->base_addr)
+ goto error;
+
+ ndev->mem_start = res->start;
+ ndev->mem_end = res->end;
spin_lock_init(&lp->reset_lock);
lp->next_tx_buf_to_use = 0x0;
dev_warn(dev, "No MAC address found\n");
/* Clear the Tx CSR's in case this is a restart */
- out_be32(lp->base_addr + XEL_TSR_OFFSET, 0);
- out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0);
+ __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET);
+ __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
/* Set the MAC address in the EmacLite device */
xemaclite_update_address(lp, ndev->dev_addr);
if (rc) {
dev_err(dev,
"Cannot register network device, aborting\n");
- goto error1;
+ goto error;
}
dev_info(dev,
(unsigned int __force)lp->base_addr, ndev->irq);
return 0;
-error1:
- release_mem_region(ndev->mem_start, resource_size(&r_mem));
-
-error2:
- xemaclite_remove_ndev(ndev);
+error:
+ xemaclite_remove_ndev(ndev, ofdev);
return rc;
}
of_node_put(lp->phy_node);
lp->phy_node = NULL;
- release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1);
-
- xemaclite_remove_ndev(ndev);
+ xemaclite_remove_ndev(ndev, of_dev);
dev_set_drvdata(dev, NULL);
return 0;
cmd->duplex = phydev->duplex;
cmd->port = PORT_MII;
cmd->phy_address = phydev->addr;
- cmd->transceiver = XCVR_EXTERNAL;
+ cmd->transceiver = phy_is_internal(phydev) ?
+ XCVR_INTERNAL : XCVR_EXTERNAL;
cmd->autoneg = phydev->autoneg;
return 0;
EXPORT_SYMBOL(phy_start_aneg);
-static void phy_change(struct work_struct *work);
-
/**
* phy_start_machine - start PHY state machine tracking
* @phydev: the phy_device struct
{
int err = 0;
- INIT_WORK(&phydev->phy_queue, phy_change);
-
atomic_set(&phydev->irq_disable, 0);
if (request_irq(phydev->irq, phy_interrupt,
IRQF_SHARED,
* phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes
* @work: work_struct that describes the work to be done
*/
-static void phy_change(struct work_struct *work)
+void phy_change(struct work_struct *work)
{
int err;
struct phy_device *phydev =
if (PHY_HALTED == phydev->state)
goto out_unlock;
- if (phydev->irq != PHY_POLL) {
+ if (phy_interrupt_is_valid(phydev)) {
/* Disable PHY Interrupts */
phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
break;
case PHY_RUNNING:
/* Only register a CHANGE if we are
- * polling */
- if (PHY_POLL == phydev->irq)
+ * polling or ignoring interrupts
+ */
+ if (!phy_interrupt_is_valid(phydev))
phydev->state = PHY_CHANGELINK;
break;
case PHY_CHANGELINK:
phydev->adjust_link(phydev->attached_dev);
- if (PHY_POLL != phydev->irq)
+ if (phy_interrupt_is_valid(phydev))
err = phy_config_interrupt(phydev,
PHY_INTERRUPT_ENABLED);
break;
schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ);
}
+void phy_mac_interrupt(struct phy_device *phydev, int new_link)
+{
+ cancel_work_sync(&phydev->phy_queue);
+ phydev->link = new_link;
+ schedule_work(&phydev->phy_queue);
+}
+EXPORT_SYMBOL(phy_mac_interrupt);
+
static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
int addr)
{
adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
idx = phy_find_setting(phydev->speed, phydev->duplex);
- if ((lp & adv & settings[idx].setting))
+ if (!(lp & adv & settings[idx].setting))
goto eee_exit;
if (clk_stop_enable) {
bool incomplete;
int i;
- port = list_first_entry(&team->port_list, struct team_port, list);
+ port = list_first_entry_or_null(&team->port_list,
+ struct team_port, list);
start_again:
err = __send_and_alloc_skb(&skb, team, portid, send_func);
err = team_nl_fill_one_port_get(skb, one_port);
if (err)
goto errout;
- } else {
- list_for_each_entry(port, &team->port_list, list) {
+ } else if (port) {
+ list_for_each_entry_from(port, &team->port_list, list) {
err = team_nl_fill_one_port_get(skb, port);
if (err) {
if (err == -EMSGSIZE) {
static int team_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
- struct net_device *dev = (struct net_device *) ptr;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct team_port *port;
port = team_port_get_rtnl(dev);
SKB_GSO_GRE = 1 << 6,
SKB_GSO_UDP_TUNNEL = 1 << 7,
+
+ SKB_GSO_MPLS = 1 << 8,
};
#if BITS_PER_LONG > 32
* @dropcount: total number of sk_receive_queue overflows
* @vlan_proto: vlan encapsulation protocol
* @vlan_tci: vlan tag control information
+ * @inner_protocol: Protocol (encapsulation)
* @inner_transport_header: Inner transport layer header (encapsulation)
* @inner_network_header: Network layer header (encapsulation)
* @inner_mac_header: Link layer header (encapsulation)
__u32 reserved_tailroom;
};
- sk_buff_data_t inner_transport_header;
- sk_buff_data_t inner_network_header;
- sk_buff_data_t inner_mac_header;
- sk_buff_data_t transport_header;
- sk_buff_data_t network_header;
- sk_buff_data_t mac_header;
+ __be16 inner_protocol;
+ __u16 inner_transport_header;
+ __u16 inner_network_header;
+ __u16 inner_mac_header;
+ __u16 transport_header;
+ __u16 network_header;
+ __u16 mac_header;
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
skb_reset_tail_pointer(skb);
skb->tail += offset;
}
+
+static inline unsigned long skb_tail_offset(const struct sk_buff *skb)
+{
+ return skb->tail;
+}
#else /* NET_SKBUFF_DATA_USES_OFFSET */
static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
{
skb->tail = skb->data + offset;
}
+static inline unsigned long skb_tail_offset(const struct sk_buff *skb)
+{
+ return skb->tail - skb->head;
+}
#endif /* NET_SKBUFF_DATA_USES_OFFSET */
/*
skb->mac_len = skb->network_header - skb->mac_header;
}
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
static inline unsigned char *skb_inner_transport_header(const struct sk_buff
*skb)
{
}
static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
{
- return skb->transport_header != ~0U;
+ return skb->transport_header != (typeof(skb->transport_header))~0U;
}
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
static inline int skb_mac_header_was_set(const struct sk_buff *skb)
{
- return skb->mac_header != ~0U;
+ return skb->mac_header != (typeof(skb->mac_header))~0U;
}
static inline void skb_reset_mac_header(struct sk_buff *skb)
skb->mac_header += offset;
}
-#else /* NET_SKBUFF_DATA_USES_OFFSET */
-static inline unsigned char *skb_inner_transport_header(const struct sk_buff
- *skb)
-{
- return skb->inner_transport_header;
-}
-
-static inline void skb_reset_inner_transport_header(struct sk_buff *skb)
-{
- skb->inner_transport_header = skb->data;
-}
-
-static inline void skb_set_inner_transport_header(struct sk_buff *skb,
- const int offset)
-{
- skb->inner_transport_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
-{
- return skb->inner_network_header;
-}
-
-static inline void skb_reset_inner_network_header(struct sk_buff *skb)
-{
- skb->inner_network_header = skb->data;
-}
-
-static inline void skb_set_inner_network_header(struct sk_buff *skb,
- const int offset)
-{
- skb->inner_network_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
-{
- return skb->inner_mac_header;
-}
-
-static inline void skb_reset_inner_mac_header(struct sk_buff *skb)
-{
- skb->inner_mac_header = skb->data;
-}
-
-static inline void skb_set_inner_mac_header(struct sk_buff *skb,
- const int offset)
-{
- skb->inner_mac_header = skb->data + offset;
-}
-static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
-{
- return skb->transport_header != NULL;
-}
-
-static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
-{
- return skb->transport_header;
-}
-
-static inline void skb_reset_transport_header(struct sk_buff *skb)
-{
- skb->transport_header = skb->data;
-}
-
-static inline void skb_set_transport_header(struct sk_buff *skb,
- const int offset)
-{
- skb->transport_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_network_header(const struct sk_buff *skb)
-{
- return skb->network_header;
-}
-
-static inline void skb_reset_network_header(struct sk_buff *skb)
-{
- skb->network_header = skb->data;
-}
-
-static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
-{
- skb->network_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
-{
- return skb->mac_header;
-}
-
-static inline int skb_mac_header_was_set(const struct sk_buff *skb)
-{
- return skb->mac_header != NULL;
-}
-
-static inline void skb_reset_mac_header(struct sk_buff *skb)
-{
- skb->mac_header = skb->data;
-}
-
-static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
-{
- skb->mac_header = skb->data + offset;
-}
-#endif /* NET_SKBUFF_DATA_USES_OFFSET */
-
static inline void skb_probe_transport_header(struct sk_buff *skb,
const int offset_hint)
{
SKB_GSO_CB(inner_skb)->mac_offset;
}
+ static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
+ {
+ int new_headroom, headroom;
+ int ret;
+
+ headroom = skb_headroom(skb);
+ ret = pskb_expand_head(skb, extra, 0, GFP_ATOMIC);
+ if (ret)
+ return ret;
+
+ new_headroom = skb_headroom(skb);
+ SKB_GSO_CB(skb)->mac_offset += (new_headroom - headroom);
+ return 0;
+ }
+
static inline bool skb_is_gso(const struct sk_buff *skb)
{
return skb_shinfo(skb)->gso_size;
#ifndef _PING_H
#define _PING_H
+#include <net/icmp.h>
#include <net/netns/hash.h>
/* PING_HTABLE_SIZE must be power of 2 */
*/
#define GID_T_MAX (((gid_t)~0U) >> 1)
- struct net_device *dev, int strict);
+/* Compatibility glue so we can support IPv6 when it's compiled as a module */
+struct pingv6_ops {
+ int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len);
+ int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg,
+ struct sk_buff *skb);
+ int (*icmpv6_err_convert)(u8 type, u8 code, int *err);
+ void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err,
+ __be16 port, u32 info, u8 *payload);
+ int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr,
++ const struct net_device *dev, int strict);
+};
+
struct ping_table {
struct hlist_nulls_head hash[PING_HTABLE_SIZE];
rwlock_t lock;
struct ping_iter_state {
struct seq_net_private p;
int bucket;
+ sa_family_t family;
};
extern struct proto ping_prot;
+extern struct ping_table ping_table;
+#if IS_ENABLED(CONFIG_IPV6)
+extern struct pingv6_ops pingv6_ops;
+#endif
+
+struct pingfakehdr {
+ struct icmphdr icmph;
+ struct iovec *iov;
+ sa_family_t family;
+ __wsum wcheck;
+};
+
+int ping_get_port(struct sock *sk, unsigned short ident);
+void ping_hash(struct sock *sk);
+void ping_unhash(struct sock *sk);
+int ping_init_sock(struct sock *sk);
+void ping_close(struct sock *sk, long timeout);
+int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+void ping_err(struct sk_buff *skb, int offset, u32 info);
+int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd,
+ struct sk_buff *);
-extern void ping_rcv(struct sk_buff *);
-extern void ping_err(struct sk_buff *, u32 info);
+int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len, int noblock, int flags, int *addr_len);
+int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
+ void *user_icmph, size_t icmph_len);
+int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len);
+int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len);
+int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+void ping_rcv(struct sk_buff *skb);
#ifdef CONFIG_PROC_FS
+struct ping_seq_afinfo {
+ char *name;
+ sa_family_t family;
+ const struct file_operations *seq_fops;
+ const struct seq_operations seq_ops;
+};
+
+extern const struct file_operations ping_seq_fops;
+
+void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family);
+void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos);
+void ping_seq_stop(struct seq_file *seq, void *v);
+int ping_proc_register(struct net *net, struct ping_seq_afinfo *afinfo);
+void ping_proc_unregister(struct net *net, struct ping_seq_afinfo *afinfo);
+
extern int __init ping_proc_init(void);
extern void ping_proc_exit(void);
#endif
void __init ping_init(void);
-
+int __init pingv6_init(void);
+void pingv6_exit(void);
#endif /* _PING_H */
* the tail pointer in struct sk_buff!
*/
memset(skb, 0, offsetof(struct sk_buff, tail));
- skb->data = NULL;
+ skb->head = NULL;
skb->truesize = sizeof(struct sk_buff);
atomic_set(&skb->users, 1);
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
- skb->mac_header = ~0U;
-#endif
+ skb->mac_header = (typeof(skb->mac_header))~0U;
out:
return skb;
}
skb->data = data;
skb_reset_tail_pointer(skb);
skb->end = skb->tail + size;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
- skb->mac_header = ~0U;
- skb->transport_header = ~0U;
-#endif
+ skb->mac_header = (typeof(skb->mac_header))~0U;
+ skb->transport_header = (typeof(skb->transport_header))~0U;
/* make sure we initialize shinfo sequentially */
shinfo = skb_shinfo(skb);
skb->data = data;
skb_reset_tail_pointer(skb);
skb->end = skb->tail + size;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
- skb->mac_header = ~0U;
- skb->transport_header = ~0U;
-#endif
+ skb->mac_header = (typeof(skb->mac_header))~0U;
+ skb->transport_header = (typeof(skb->transport_header))~0U;
/* make sure we initialize shinfo sequentially */
shinfo = skb_shinfo(skb);
static void skb_release_all(struct sk_buff *skb)
{
skb_release_head_state(skb);
- if (likely(skb->data))
+ if (likely(skb->head))
skb_release_data(skb);
}
doffset + tnl_hlen);
if (fskb != skb_shinfo(skb)->frag_list)
- continue;
+ goto perform_csum_check;
if (!sg) {
nskb->ip_summed = CHECKSUM_NONE;
nskb->len += nskb->data_len;
nskb->truesize += nskb->data_len;
+perform_csum_check:
if (!csum) {
nskb->csum = skb_checksum(nskb, doffset,
nskb->len - doffset, 0);
EXPORT_SYMBOL_GPL(ip_tunnel_rcv);
void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
- const struct iphdr *tnl_params)
+ const struct iphdr *tnl_params, const u8 protocol)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
const struct iphdr *inner_iph;
inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
+ memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
dst = tnl_params->daddr;
if (dst == 0) {
/* NBMA tunnel */
skb_dst_drop(skb);
skb_dst_set(skb, &rt->dst);
- memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
/* Push down and install the IP header. */
skb_push(skb, sizeof(struct iphdr));
iph->version = 4;
iph->ihl = sizeof(struct iphdr) >> 2;
iph->frag_off = df;
- iph->protocol = tnl_params->protocol;
+ iph->protocol = protocol;
iph->tos = ip_tunnel_ecn_encap(tos, inner_iph, skb);
iph->daddr = fl4.daddr;
iph->saddr = fl4.saddr;
return hval & (FNHE_HASH_SIZE - 1);
}
+static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
+{
+ rt->rt_pmtu = fnhe->fnhe_pmtu;
+ rt->dst.expires = fnhe->fnhe_expires;
+
+ if (fnhe->fnhe_gw) {
+ rt->rt_flags |= RTCF_REDIRECTED;
+ rt->rt_gateway = fnhe->fnhe_gw;
+ rt->rt_uses_gateway = 1;
+ }
+}
+
static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
u32 pmtu, unsigned long expires)
{
struct fnhe_hash_bucket *hash;
struct fib_nh_exception *fnhe;
+ struct rtable *rt;
+ unsigned int i;
int depth;
u32 hval = fnhe_hashfun(daddr);
fnhe->fnhe_gw = gw;
if (pmtu) {
fnhe->fnhe_pmtu = pmtu;
- fnhe->fnhe_expires = expires;
+ fnhe->fnhe_expires = max(1UL, expires);
}
+ /* Update all cached dsts too */
+ rt = rcu_dereference(fnhe->fnhe_rth);
+ if (rt)
+ fill_route_from_fnhe(rt, fnhe);
} else {
if (depth > FNHE_RECLAIM_DEPTH)
fnhe = fnhe_oldest(hash);
fnhe->fnhe_next = hash->chain;
rcu_assign_pointer(hash->chain, fnhe);
}
+ fnhe->fnhe_genid = fnhe_genid(dev_net(nh->nh_dev));
fnhe->fnhe_daddr = daddr;
fnhe->fnhe_gw = gw;
fnhe->fnhe_pmtu = pmtu;
fnhe->fnhe_expires = expires;
+
+ /* Exception created; mark the cached routes for the nexthop
+ * stale, so anyone caching it rechecks if this exception
+ * applies to them.
+ */
+ for_each_possible_cpu(i) {
+ struct rtable __rcu **prt;
+ prt = per_cpu_ptr(nh->nh_pcpu_rth_output, i);
+ rt = rcu_dereference(*prt);
+ if (rt)
+ rt->dst.obsolete = DST_OBSOLETE_KILL;
+ }
}
fnhe->fnhe_stamp = jiffies;
{
struct rtable *rt;
struct flowi4 fl4;
+ const struct iphdr *iph = (const struct iphdr *) skb->data;
+ int oif = skb->dev->ifindex;
+ u8 tos = RT_TOS(iph->tos);
+ u8 prot = iph->protocol;
+ u32 mark = skb->mark;
rt = (struct rtable *) dst;
- ip_rt_build_flow_key(&fl4, sk, skb);
+ __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
if (mtu < ip_rt_min_pmtu)
mtu = ip_rt_min_pmtu;
- if (!rt->rt_pmtu) {
- dst->obsolete = DST_OBSOLETE_KILL;
- } else {
- rt->rt_pmtu = mtu;
- dst->expires = max(1UL, jiffies + ip_rt_mtu_expires);
- }
+ if (rt->rt_pmtu == mtu &&
+ time_before(jiffies, dst->expires - ip_rt_mtu_expires / 2))
+ return;
rcu_read_lock();
if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) {
* DST_OBSOLETE_FORCE_CHK which forces validation calls down
* into this function always.
*
- * When a PMTU/redirect information update invalidates a
- * route, this is indicated by setting obsolete to
- * DST_OBSOLETE_KILL.
+ * When a PMTU/redirect information update invalidates a route,
+ * this is indicated by setting obsolete to DST_OBSOLETE_KILL or
+ * DST_OBSOLETE_DEAD by dst_free().
*/
- if (dst->obsolete == DST_OBSOLETE_KILL || rt_is_expired(rt))
+ if (dst->obsolete != DST_OBSOLETE_FORCE_CHK || rt_is_expired(rt))
return NULL;
return dst;
}
spin_lock_bh(&fnhe_lock);
if (daddr == fnhe->fnhe_daddr) {
+ int genid = fnhe_genid(dev_net(rt->dst.dev));
struct rtable *orig = rcu_dereference(fnhe->fnhe_rth);
- if (orig && rt_is_expired(orig)) {
+
+ if (fnhe->fnhe_genid != genid) {
+ fnhe->fnhe_genid = genid;
fnhe->fnhe_gw = 0;
fnhe->fnhe_pmtu = 0;
fnhe->fnhe_expires = 0;
}
- if (fnhe->fnhe_pmtu) {
- unsigned long expires = fnhe->fnhe_expires;
- unsigned long diff = expires - jiffies;
-
- if (time_before(jiffies, expires)) {
- rt->rt_pmtu = fnhe->fnhe_pmtu;
- dst_set_expires(&rt->dst, diff);
- }
- }
- if (fnhe->fnhe_gw) {
- rt->rt_flags |= RTCF_REDIRECTED;
- rt->rt_gateway = fnhe->fnhe_gw;
- rt->rt_uses_gateway = 1;
- } else if (!rt->rt_gateway)
+ fill_route_from_fnhe(rt, fnhe);
+ if (!rt->rt_gateway)
rt->rt_gateway = daddr;
rcu_assign_pointer(fnhe->fnhe_rth, rt);
void __user *buffer,
size_t *lenp, loff_t *ppos)
{
+ struct net *net = (struct net *)__ctl->extra1;
+
if (write) {
- rt_cache_flush((struct net *)__ctl->extra1);
+ rt_cache_flush(net);
+ fnhe_genid_bump(net);
return 0;
}
static __net_init int rt_genid_init(struct net *net)
{
atomic_set(&net->rt_genid, 0);
+ atomic_set(&net->fnhe_genid, 0);
get_random_bytes(&net->ipv4.dev_addr_genid,
sizeof(net->ipv4.dev_addr_genid));
return 0;
ift = !max_addresses ||
ipv6_count_addresses(idev) < max_addresses ?
- ipv6_add_addr(idev, &addr, tmp_plen,
- ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
+ ipv6_add_addr(idev, &addr, tmp_plen, ipv6_addr_scope(&addr),
addr_flags) : NULL;
if (IS_ERR_OR_NULL(ift)) {
in6_ifa_put(ifp);
}
int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
- struct net_device *dev, int strict)
+ const struct net_device *dev, int strict)
{
struct inet6_ifaddr *ifp;
unsigned int hash = inet6_addr_hash(addr);
* Manual configuration of address on an interface
*/
static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx,
+ const struct in6_addr *peer_pfx,
unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
__u32 valid_lft)
{
ifp->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
ifp->tstamp = jiffies;
+ if (peer_pfx)
+ ifp->peer_addr = *peer_pfx;
spin_unlock_bh(&ifp->lock);
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
return -EFAULT;
rtnl_lock();
- err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr,
+ err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL,
ireq.ifr6_prefixlen, IFA_F_PERMANENT,
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
rtnl_unlock();
sp_rt = addrconf_dst_alloc(idev, &sp_ifa->addr, 0);
/* Failure cases are ignored */
- if (!IS_ERR(sp_rt))
+ if (!IS_ERR(sp_rt)) {
+ sp_ifa->rt = sp_rt;
ip6_ins_rt(sp_rt);
+ }
}
read_unlock_bh(&idev->lock);
}
}
static int addrconf_notify(struct notifier_block *this, unsigned long event,
- void *data)
+ void *ptr)
{
- struct net_device *dev = (struct net_device *) data;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct inet6_dev *idev = __in6_dev_get(dev);
int run_pending = 0;
int err;
rcu_read_unlock_bh();
}
-static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
+static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local,
+ struct in6_addr **peer_pfx)
{
struct in6_addr *pfx = NULL;
+ *peer_pfx = NULL;
+
if (addr)
pfx = nla_data(addr);
if (local) {
if (pfx && nla_memcmp(local, pfx, sizeof(*pfx)))
- pfx = NULL;
- else
- pfx = nla_data(local);
+ *peer_pfx = pfx;
+ pfx = nla_data(local);
}
return pfx;
struct net *net = sock_net(skb->sk);
struct ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
- struct in6_addr *pfx;
+ struct in6_addr *pfx, *peer_pfx;
int err;
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
return err;
ifm = nlmsg_data(nlh);
- pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+ pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
if (pfx == NULL)
return -EINVAL;
struct net *net = sock_net(skb->sk);
struct ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
- struct in6_addr *pfx;
+ struct in6_addr *pfx, *peer_pfx;
struct inet6_ifaddr *ifa;
struct net_device *dev;
u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
return err;
ifm = nlmsg_data(nlh);
- pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+ pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
if (pfx == NULL)
return -EINVAL;
* It would be best to check for !NLM_F_CREATE here but
* userspace alreay relies on not having to provide this.
*/
- return inet6_addr_add(net, ifm->ifa_index, pfx,
+ return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
ifm->ifa_prefixlen, ifa_flags,
preferred_lft, valid_lft);
}
static inline int inet6_ifaddr_msgsize(void)
{
return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+ + nla_total_size(16) /* IFA_LOCAL */
+ nla_total_size(16) /* IFA_ADDRESS */
+ nla_total_size(sizeof(struct ifa_cacheinfo));
}
valid = INFINITY_LIFE_TIME;
}
- if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 ||
- put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) {
- nlmsg_cancel(skb, nlh);
- return -EMSGSIZE;
- }
+ if (!ipv6_addr_any(&ifa->peer_addr)) {
+ if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 ||
+ nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0)
+ goto error;
+ } else
+ if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0)
+ goto error;
+
+ if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
+ goto error;
return nlmsg_end(skb, nlh);
+
+error:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
}
static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
struct net *net = sock_net(in_skb->sk);
struct ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
- struct in6_addr *addr = NULL;
+ struct in6_addr *addr = NULL, *peer;
struct net_device *dev = NULL;
struct inet6_ifaddr *ifa;
struct sk_buff *skb;
if (err < 0)
goto errout;
- addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+ addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);
if (addr == NULL) {
err = -EINVAL;
goto errout;
ip6_ins_rt(ifp->rt);
if (ifp->idev->cnf.forwarding)
addrconf_join_anycast(ifp);
+ if (!ipv6_addr_any(&ifp->peer_addr))
+ addrconf_prefix_route(&ifp->peer_addr, 128,
+ ifp->idev->dev, 0, 0);
break;
case RTM_DELADDR:
if (ifp->idev->cnf.forwarding)
addrconf_leave_anycast(ifp);
addrconf_leave_solict(ifp->idev, &ifp->addr);
+ if (!ipv6_addr_any(&ifp->peer_addr)) {
+ struct rt6_info *rt;
+ struct net_device *dev = ifp->idev->dev;
+
+ rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL,
+ dev->ifindex, 1);
+ if (rt) {
+ dst_hold(&rt->dst);
+ if (ip6_del_rt(rt))
+ dst_free(&rt->dst);
+ }
+ }
dst_hold(&ifp->rt->dst);
if (ip6_del_rt(ifp->rt))
static void dev_disable_change(struct inet6_dev *idev)
{
+ struct netdev_notifier_info info;
+
if (!idev || !idev->dev)
return;
+ netdev_notifier_info_init(&info, idev->dev);
if (idev->cnf.disable_ipv6)
- addrconf_notify(NULL, NETDEV_DOWN, idev->dev);
+ addrconf_notify(NULL, NETDEV_DOWN, &info);
else
- addrconf_notify(NULL, NETDEV_UP, idev->dev);
+ addrconf_notify(NULL, NETDEV_UP, &info);
}
static void addrconf_disable_change(struct net *net, __s32 newf)
--- /dev/null
- struct net_device *dev, int strict)
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * "Ping" sockets
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Based on ipv4/ping.c code.
+ *
+ * Authors: Lorenzo Colitti (IPv6 support)
+ * Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6),
+ * Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32)
+ *
+ */
+
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/transp_v6.h>
+#include <net/ping.h>
+
+struct proto pingv6_prot = {
+ .name = "PINGv6",
+ .owner = THIS_MODULE,
+ .init = ping_init_sock,
+ .close = ping_close,
+ .connect = ip6_datagram_connect,
+ .disconnect = udp_disconnect,
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .sendmsg = ping_v6_sendmsg,
+ .recvmsg = ping_recvmsg,
+ .bind = ping_bind,
+ .backlog_rcv = ping_queue_rcv_skb,
+ .hash = ping_hash,
+ .unhash = ping_unhash,
+ .get_port = ping_get_port,
+ .obj_size = sizeof(struct raw6_sock),
+};
+EXPORT_SYMBOL_GPL(pingv6_prot);
+
+static struct inet_protosw pingv6_protosw = {
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_ICMPV6,
+ .prot = &pingv6_prot,
+ .ops = &inet6_dgram_ops,
+ .no_check = UDP_CSUM_DEFAULT,
+ .flags = INET_PROTOSW_REUSE,
+};
+
+
+/* Compatibility glue so we can support IPv6 when it's compiled as a module */
+int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+ return -EAFNOSUPPORT;
+}
+int dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg,
+ struct sk_buff *skb)
+{
+ return -EAFNOSUPPORT;
+}
+int dummy_icmpv6_err_convert(u8 type, u8 code, int *err)
+{
+ return -EAFNOSUPPORT;
+}
+void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
+ __be16 port, u32 info, u8 *payload) {}
+int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
++ const struct net_device *dev, int strict)
+{
+ return 0;
+}
+
+int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len)
+{
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct icmp6hdr user_icmph;
+ int addr_type;
+ struct in6_addr *daddr;
+ int iif = 0;
+ struct flowi6 fl6;
+ int err;
+ int hlimit;
+ struct dst_entry *dst;
+ struct rt6_info *rt;
+ struct pingfakehdr pfh;
+
+ pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
+
+ err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph,
+ sizeof(user_icmph));
+ if (err)
+ return err;
+
+ if (msg->msg_name) {
+ struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name;
+ if (msg->msg_namelen < sizeof(struct sockaddr_in6) ||
+ u->sin6_family != AF_INET6) {
+ return -EINVAL;
+ }
+ if (sk->sk_bound_dev_if &&
+ sk->sk_bound_dev_if != u->sin6_scope_id) {
+ return -EINVAL;
+ }
+ daddr = &(u->sin6_addr);
+ iif = u->sin6_scope_id;
+ } else {
+ if (sk->sk_state != TCP_ESTABLISHED)
+ return -EDESTADDRREQ;
+ daddr = &np->daddr;
+ }
+
+ if (!iif)
+ iif = sk->sk_bound_dev_if;
+
+ addr_type = ipv6_addr_type(daddr);
+ if (__ipv6_addr_needs_scope_id(addr_type) && !iif)
+ return -EINVAL;
+ if (addr_type & IPV6_ADDR_MAPPED)
+ return -EINVAL;
+
+ /* TODO: use ip6_datagram_send_ctl to get options from cmsg */
+
+ memset(&fl6, 0, sizeof(fl6));
+
+ fl6.flowi6_proto = IPPROTO_ICMPV6;
+ fl6.saddr = np->saddr;
+ fl6.daddr = *daddr;
+ fl6.fl6_icmp_type = user_icmph.icmp6_type;
+ fl6.fl6_icmp_code = user_icmph.icmp6_code;
+ security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
+
+ if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+ fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
+
+ dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1);
+ if (IS_ERR(dst))
+ return PTR_ERR(dst);
+ rt = (struct rt6_info *) dst;
+
+ np = inet6_sk(sk);
+ if (!np)
+ return -EBADF;
+
+ if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+ fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
+
+ pfh.icmph.type = user_icmph.icmp6_type;
+ pfh.icmph.code = user_icmph.icmp6_code;
+ pfh.icmph.checksum = 0;
+ pfh.icmph.un.echo.id = inet->inet_sport;
+ pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence;
+ pfh.iov = msg->msg_iov;
+ pfh.wcheck = 0;
+ pfh.family = AF_INET6;
+
+ if (ipv6_addr_is_multicast(&fl6.daddr))
+ hlimit = np->mcast_hops;
+ else
+ hlimit = np->hop_limit;
+ if (hlimit < 0)
+ hlimit = ip6_dst_hoplimit(dst);
+
+ err = ip6_append_data(sk, ping_getfrag, &pfh, len,
+ 0, hlimit,
+ np->tclass, NULL, &fl6, rt,
+ MSG_DONTWAIT, np->dontfrag);
+
+ if (err) {
+ ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev,
+ ICMP6_MIB_OUTERRORS);
+ ip6_flush_pending_frames(sk);
+ } else {
+ err = icmpv6_push_pending_frames(sk, &fl6,
+ (struct icmp6hdr *) &pfh.icmph,
+ len);
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_PROC_FS
+static void *ping_v6_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ return ping_seq_start(seq, pos, AF_INET6);
+}
+
+int ping_v6_seq_show(struct seq_file *seq, void *v)
+{
+ if (v == SEQ_START_TOKEN) {
+ seq_puts(seq, IPV6_SEQ_DGRAM_HEADER);
+ } else {
+ int bucket = ((struct ping_iter_state *) seq->private)->bucket;
+ struct inet_sock *inet = inet_sk(v);
+ __u16 srcp = ntohs(inet->inet_sport);
+ __u16 destp = ntohs(inet->inet_dport);
+ ip6_dgram_sock_seq_show(seq, v, srcp, destp, bucket);
+ }
+ return 0;
+}
+
+static struct ping_seq_afinfo ping_v6_seq_afinfo = {
+ .name = "icmp6",
+ .family = AF_INET6,
+ .seq_fops = &ping_seq_fops,
+ .seq_ops = {
+ .start = ping_v6_seq_start,
+ .show = ping_v6_seq_show,
+ .next = ping_seq_next,
+ .stop = ping_seq_stop,
+ },
+};
+
+static int __net_init ping_v6_proc_init_net(struct net *net)
+{
+ return ping_proc_register(net, &ping_v6_seq_afinfo);
+}
+
+static void __net_init ping_v6_proc_exit_net(struct net *net)
+{
+ return ping_proc_unregister(net, &ping_v6_seq_afinfo);
+}
+
+static struct pernet_operations ping_v6_net_ops = {
+ .init = ping_v6_proc_init_net,
+ .exit = ping_v6_proc_exit_net,
+};
+#endif
+
+int __init pingv6_init(void)
+{
+#ifdef CONFIG_PROC_FS
+ int ret = register_pernet_subsys(&ping_v6_net_ops);
+ if (ret)
+ return ret;
+#endif
+ pingv6_ops.ipv6_recv_error = ipv6_recv_error;
+ pingv6_ops.ip6_datagram_recv_ctl = ip6_datagram_recv_ctl;
+ pingv6_ops.icmpv6_err_convert = icmpv6_err_convert;
+ pingv6_ops.ipv6_icmp_error = ipv6_icmp_error;
+ pingv6_ops.ipv6_chk_addr = ipv6_chk_addr;
+ return inet6_register_protosw(&pingv6_protosw);
+}
+
+/* This never gets called because it's not possible to unload the ipv6 module,
+ * but just in case.
+ */
+void pingv6_exit(void)
+{
+ pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error;
+ pingv6_ops.ip6_datagram_recv_ctl = dummy_ip6_datagram_recv_ctl;
+ pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert;
+ pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error;
+ pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr;
+#ifdef CONFIG_PROC_FS
+ unregister_pernet_subsys(&ping_v6_net_ops);
+#endif
+ inet6_unregister_protosw(&pingv6_protosw);
+}
unsigned int mss;
unsigned int unfrag_ip6hlen, unfrag_len;
struct frag_hdr *fptr;
- u8 *mac_start, *prevhdr;
+ u8 *packet_start, *prevhdr;
u8 nexthdr;
u8 frag_hdr_sz = sizeof(struct frag_hdr);
int offset;
__wsum csum;
+ int tnl_hlen;
mss = skb_shinfo(skb)->gso_size;
if (unlikely(skb->len <= mss))
if (unlikely(type & ~(SKB_GSO_UDP |
SKB_GSO_DODGY |
SKB_GSO_UDP_TUNNEL |
- SKB_GSO_GRE) ||
+ SKB_GSO_GRE |
+ SKB_GSO_MPLS) ||
!(type & (SKB_GSO_UDP))))
goto out;
skb->ip_summed = CHECKSUM_NONE;
/* Check if there is enough headroom to insert fragment header. */
- if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
- pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
- goto out;
+ tnl_hlen = skb_tnl_header_len(skb);
+ if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) {
+ if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
+ goto out;
+ }
/* Find the unfragmentable header and shift it left by frag_hdr_sz
* bytes to insert fragment header.
unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
nexthdr = *prevhdr;
*prevhdr = NEXTHDR_FRAGMENT;
- unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
- unfrag_ip6hlen;
- mac_start = skb_mac_header(skb);
- memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
+ unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
+ unfrag_ip6hlen + tnl_hlen;
+ packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
+ memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
+ SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
skb->mac_header -= frag_hdr_sz;
skb->network_header -= frag_hdr_sz;
return 0;
}
- static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr)
+ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *iter;
u64 new, mask, tmp;
u8 *m;
int ret = 0;
mutex_lock(&local->iflist_mtx);
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ list_for_each_entry(iter, &local->interfaces, list) {
+ if (iter == sdata)
+ continue;
+
+ if (iter->vif.type == NL80211_IFTYPE_MONITOR)
continue;
- m = sdata->vif.addr;
+ m = iter->vif.addr;
tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
if (ieee80211_sdata_running(sdata))
return -EBUSY;
- ret = ieee80211_verify_mac(sdata->local, sa->sa_data);
+ ret = ieee80211_verify_mac(sdata, sa->sa_data);
if (ret)
return ret;
master->control_port_protocol;
sdata->control_port_no_encrypt =
master->control_port_no_encrypt;
+ sdata->vif.cab_queue = master->vif.cab_queue;
+ memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
+ sizeof(sdata->vif.hw_queue));
break;
}
case NL80211_IFTYPE_AP:
ieee80211_recalc_ps(local, -1);
- if (dev) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ /* XXX: for AP_VLAN, actually track AP queues */
+ netif_tx_start_all_queues(dev);
+ } else if (dev) {
unsigned long flags;
int n_acs = IEEE80211_NUM_ACS;
int ac;
break;
}
+ /*
+ * Pick address of existing interface in case user changed
+ * MAC address manually, default to perm_addr.
+ */
m = local->hw.wiphy->perm_addr;
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ continue;
+ m = sdata->vif.addr;
+ break;
+ }
start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
ASSERT_RTNL();
+ /*
+ * Close all AP_VLAN interfaces first, as otherwise they
+ * might be closed while the AP interface they belong to
+ * is closed, causing unregister_netdevice_many() to crash.
+ */
+ list_for_each_entry(sdata, &local->interfaces, list)
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ dev_close(sdata->dev);
+
mutex_lock(&local->iflist_mtx);
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
list_del(&sdata->list);
}
static int netdev_notify(struct notifier_block *nb,
- unsigned long state,
- void *ndev)
+ unsigned long state, void *ptr)
{
- struct net_device *dev = ndev;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct ieee80211_sub_if_data *sdata;
if (state != NETDEV_CHANGENAME)
struct qdisc_watchdog watchdog; /* Watchdog timer */
};
+
+/* GSO packet is too big, segment it so that tbf can transmit
+ * each segment in time
+ */
+static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct tbf_sched_data *q = qdisc_priv(sch);
+ struct sk_buff *segs, *nskb;
+ netdev_features_t features = netif_skb_features(skb);
+ int ret, nb;
+
+ segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+
+ if (IS_ERR_OR_NULL(segs))
+ return qdisc_reshape_fail(skb, sch);
+
+ nb = 0;
+ while (segs) {
+ nskb = segs->next;
+ segs->next = NULL;
+ if (likely(segs->len <= q->max_size)) {
+ qdisc_skb_cb(segs)->pkt_len = segs->len;
+ ret = qdisc_enqueue(segs, q->qdisc);
+ } else {
+ ret = qdisc_reshape_fail(skb, sch);
+ }
+ if (ret != NET_XMIT_SUCCESS) {
+ if (net_xmit_drop_count(ret))
+ sch->qstats.drops++;
+ } else {
+ nb++;
+ }
+ segs = nskb;
+ }
+ sch->q.qlen += nb;
+ if (nb > 1)
+ qdisc_tree_decrease_qlen(sch, 1 - nb);
+ consume_skb(skb);
+ return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
+}
+
static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct tbf_sched_data *q = qdisc_priv(sch);
int ret;
- if (qdisc_pkt_len(skb) > q->max_size)
+ if (qdisc_pkt_len(skb) > q->max_size) {
+ if (skb_is_gso(skb))
+ return tbf_segment(skb, sch);
return qdisc_reshape_fail(skb, sch);
-
+ }
ret = qdisc_enqueue(skb, q->qdisc);
if (ret != NET_XMIT_SUCCESS) {
if (net_xmit_drop_count(ret))
q->tokens = q->buffer;
q->ptokens = q->mtu;
- psched_ratecfg_precompute(&q->rate, rtab->rate.rate);
+ psched_ratecfg_precompute(&q->rate, &rtab->rate);
if (ptab) {
- psched_ratecfg_precompute(&q->peak, ptab->rate.rate);
+ psched_ratecfg_precompute(&q->peak, &ptab->rate);
q->peak_present = true;
} else {
q->peak_present = false;
goto nla_put_failure;
opt.limit = q->limit;
- opt.rate.rate = psched_ratecfg_getrate(&q->rate);
+ psched_ratecfg_getrate(&opt.rate, &q->rate);
if (q->peak_present)
- opt.peakrate.rate = psched_ratecfg_getrate(&q->peak);
+ psched_ratecfg_getrate(&opt.peakrate, &q->peak);
else
memset(&opt.peakrate, 0, sizeof(opt.peakrate));
opt.mtu = PSCHED_NS2TICKS(q->mtu);
}
}
- static void xfrm_garbage_collect(struct net *net)
+ void xfrm_garbage_collect(struct net *net)
{
flow_cache_flush();
__xfrm_garbage_collect(net);
}
+ EXPORT_SYMBOL(xfrm_garbage_collect);
static void xfrm_garbage_collect_deferred(struct net *net)
{
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
- struct net_device *dev = ptr;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
switch (event) {
case NETDEV_DOWN: