From bf32fecdc1851ad9ca960f56771b798d17c26cf1 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Mon, 2 Apr 2012 14:26:27 -0700 Subject: openvswitch: Add length check when retrieving TCP flags. When collecting TCP flags we check that the IP header indicates that a TCP header is present but not that the packet is actually long enough to contain the header. This adds a check to prevent reading off the end of the packet. In practice, this is only likely to result in reading of bad data and not a crash due to the presence of struct skb_shared_info at the end of the packet. Signed-off-by: Jesse Gross --- net/openvswitch/flow.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 1252c3081ef1..2a11ec2383ee 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -183,7 +183,8 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) u8 tcp_flags = 0; if (flow->key.eth.type == htons(ETH_P_IP) && - flow->key.ip.proto == IPPROTO_TCP) { + flow->key.ip.proto == IPPROTO_TCP && + likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) { u8 *tcp = (u8 *)tcp_hdr(skb); tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK; } -- cgit v1.2.3 From cf04317227d0c53d931a0b963e7ac6f7f0125e8a Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Mon, 30 Apr 2012 11:43:07 -0700 Subject: brcmfmac: fix a double spin_unlock_irqrestore issue in dpc dpc_tl_lock is not acquired in the error handle code for bus down. But it's unlocked using spin_unlock_irqrestore after finishing task list walk down. Grab the lock before breaking the loop to avoid a double unlock. Reported-by: Dan Carpenter Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index eb3829b03cd3..e2b34e1563f4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2637,6 +2637,7 @@ static int brcmf_sdbrcm_dpc_thread(void *data) /* after stopping the bus, exit thread */ brcmf_sdbrcm_bus_stop(bus->sdiodev->dev); bus->dpc_tsk = NULL; + spin_lock_irqsave(&bus->dpc_tl_lock, flags); break; } -- cgit v1.2.3 From 9d1ceac5c6f7e77ea914de376ec17ced3eb22e5f Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 1 May 2012 09:12:24 +0530 Subject: Revert "ath9k_hw: Fix incorrect spur_freq_sd for AR9003" This reverts commit a844adfd7bee4edc66d337de6c33b348e83552a8. The commit a844adfd is degrading rx sensitivity of lower rate in HT40 mode and it is confirmed that reverting the change is improving rx sensitivity. spur_freq_sd (for self-corr in AGC) is defined with respect to the center of each 20MHz channel while spur_phase_delta (for self-corr in Rx and spur data filter) is defined with respect to the center of current RF channel. So in short, we need to subtract spur_freq_sd (for self-corr in AGC) by the offset between the center of primary20 and the center of RF channel in SW. This offset could be +/10 MHz for dynamic 40. Cc: Madhan Jaganathan Signed-off-by: Kai Shi Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index deb6cfb2959a..600aca9fe6b1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -373,7 +373,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 0; - spur_freq_sd = (freq_offset << 9) / 11; + spur_freq_sd = ((freq_offset + 10) << 9) / 11; } else { if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, @@ -382,7 +382,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 1; - spur_freq_sd = (freq_offset << 9) / 11; + spur_freq_sd = ((freq_offset - 10) << 9) / 11; } -- cgit v1.2.3 From ab6039a70bb559b2fb5f363934da67d864ccda2a Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Sat, 17 Mar 2012 05:51:52 +0000 Subject: ixgbe: fix race condition with shutdown It was possible for shutdown to pull the rug out from other driver entry points. Now we just grab the rtnl lock before taking everything apart. Thanks to Hariharan for noticing this tight race condition. Signed-off-by: Don Skidmore Cc: Hariharan Nagarajan Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 88f6b2e9b72d..d9dbf871abb8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -4866,10 +4866,12 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake) netif_device_detach(netdev); if (netif_running(netdev)) { + rtnl_lock(); ixgbe_down(adapter); ixgbe_free_irq(adapter); ixgbe_free_all_tx_resources(adapter); ixgbe_free_all_rx_resources(adapter); + rtnl_unlock(); } ixgbe_clear_interrupt_scheme(adapter); -- cgit v1.2.3 From f525c6d29501eb483f2d6f742d2f1dc0452b924d Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Wed, 18 Apr 2012 22:42:27 +0000 Subject: ixgbe: dcb: BIT_APP_UPCHG not set by ixgbe_copy_dcb_cfg() After this commit: commit aacc1bea190d731755a65cb8ec31dd756f4e263e Author: Multanen, Eric W Date: Wed Mar 28 07:49:09 2012 +0000 ixgbe: driver fix for link flap The BIT_APP_UPCHG bit is no longer set when ixgbe_dcbnl_set_all() is called. This results in the FCoE app user priority never getting set and the driver will not configure the tx_rings correctly for FCoE packets which use the SAN MTU and FCoE offloads. We resolve this regression by fixing ixgbe_copy_dcb_cfg() to also check for FCoE application changes. Additionally, we can drop the IEEE variants of get_dcb_app() because this path is never called with the IEEE mode enabled. Signed-off-by: John Fastabend Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 3 -- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 43 ++++++++++++------------- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 74e192107f9a..81b155589532 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -574,9 +574,6 @@ extern struct ixgbe_info ixgbe_82599_info; extern struct ixgbe_info ixgbe_X540_info; #ifdef CONFIG_IXGBE_DCB extern const struct dcbnl_rtnl_ops dcbnl_ops; -extern int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg, - struct ixgbe_dcb_config *dst_dcb_cfg, - int tc_max); #endif extern char ixgbe_driver_name[]; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index 652e4b09546d..32e5c02ff6d0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -44,18 +44,26 @@ #define DCB_NO_HW_CHG 1 /* DCB configuration did not change */ #define DCB_HW_CHG 2 /* DCB configuration changed, no reset */ -int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *scfg, - struct ixgbe_dcb_config *dcfg, int tc_max) +static int ixgbe_copy_dcb_cfg(struct ixgbe_adapter *adapter, int tc_max) { + struct ixgbe_dcb_config *scfg = &adapter->temp_dcb_cfg; + struct ixgbe_dcb_config *dcfg = &adapter->dcb_cfg; struct tc_configuration *src = NULL; struct tc_configuration *dst = NULL; int i, j; int tx = DCB_TX_CONFIG; int rx = DCB_RX_CONFIG; int changes = 0; +#ifdef IXGBE_FCOE + struct dcb_app app = { + .selector = DCB_APP_IDTYPE_ETHTYPE, + .protocol = ETH_P_FCOE, + }; + u8 up = dcb_getapp(adapter->netdev, &app); - if (!scfg || !dcfg) - return changes; + if (up && !(up & (1 << adapter->fcoe.up))) + changes |= BIT_APP_UPCHG; +#endif for (i = DCB_PG_ATTR_TC_0; i < tc_max + DCB_PG_ATTR_TC_0; i++) { src = &scfg->tc_config[i - DCB_PG_ATTR_TC_0]; @@ -332,28 +340,12 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) struct ixgbe_adapter *adapter = netdev_priv(netdev); int ret = DCB_NO_HW_CHG; int i; -#ifdef IXGBE_FCOE - struct dcb_app app = { - .selector = DCB_APP_IDTYPE_ETHTYPE, - .protocol = ETH_P_FCOE, - }; - u8 up; - - /* In IEEE mode, use the IEEE Ethertype selector value */ - if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) { - app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; - up = dcb_ieee_getapp_mask(netdev, &app); - } else { - up = dcb_getapp(netdev, &app); - } -#endif /* Fail command if not in CEE mode */ if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE)) return ret; - adapter->dcb_set_bitmap |= ixgbe_copy_dcb_cfg(&adapter->temp_dcb_cfg, - &adapter->dcb_cfg, + adapter->dcb_set_bitmap |= ixgbe_copy_dcb_cfg(adapter, MAX_TRAFFIC_CLASS); if (!adapter->dcb_set_bitmap) return ret; @@ -440,8 +432,13 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) * FCoE is using changes. This happens if the APP info * changes or the up2tc mapping is updated. */ - if ((up && !(up & (1 << adapter->fcoe.up))) || - (adapter->dcb_set_bitmap & BIT_APP_UPCHG)) { + if (adapter->dcb_set_bitmap & BIT_APP_UPCHG) { + struct dcb_app app = { + .selector = DCB_APP_IDTYPE_ETHTYPE, + .protocol = ETH_P_FCOE, + }; + u8 up = dcb_getapp(netdev, &app); + adapter->fcoe.up = ffs(up) - 1; ixgbe_dcbnl_devreset(netdev); ret = DCB_HW_CHG_RST; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d9dbf871abb8..50f0700d250d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -4418,8 +4418,8 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) adapter->dcb_cfg.pfc_mode_enable = false; adapter->dcb_set_bitmap = 0x00; adapter->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE; - ixgbe_copy_dcb_cfg(&adapter->dcb_cfg, &adapter->temp_dcb_cfg, - MAX_TRAFFIC_CLASS); + memcpy(&adapter->temp_dcb_cfg, &adapter->dcb_cfg, + sizeof(adapter->temp_dcb_cfg)); #endif -- cgit v1.2.3 From ed90542b0ce5415050c6fbfca324bccaafa69f2f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 24 Mar 2012 00:29:46 -0400 Subject: iwlwifi: fix skb truesize underestimation By default, iwlwifi uses order-1 pages (8 KB) to store incoming frames, but doesnt say so in skb->truesize. This makes very possible to exhaust kernel memory since these skb evade normal socket memory accounting. As struct ieee80211_hdr is going to be pulled before calling IP stack, there is no need to use dev_alloc_skb() to reserve NET_SKB_PAD bytes. alloc_skb() is ok in this driver, allowing more tailroom. Pull beginning of frame in skb header, in the hope we can reuse order-1 pages in the driver immediately for small frames and reduce their truesize to the minimum (linear skbs) Signed-off-by: Eric Dumazet Cc: Wey-Yi Guy Cc: "John W. Linville" Cc: Neal Cardwell Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rx.c | 21 ++++++++++++++------- drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c | 3 ++- drivers/net/wireless/iwlwifi/iwl-trans.h | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index f4b84d1596e3..22474608a70b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -773,8 +773,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, struct sk_buff *skb; __le16 fc = hdr->frame_control; struct iwl_rxon_context *ctx; - struct page *p; - int offset; + unsigned int hdrlen, fraglen; /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { @@ -788,16 +787,24 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; - skb = dev_alloc_skb(128); + /* Dont use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); if (!skb) { - IWL_ERR(priv, "dev_alloc_skb failed\n"); + IWL_ERR(priv, "alloc_skb failed\n"); return; } + hdrlen = min_t(unsigned int, len, skb_tailroom(skb)); + memcpy(skb_put(skb, hdrlen), hdr, hdrlen); + fraglen = len - hdrlen; - offset = (void *)hdr - rxb_addr(rxb); - p = rxb_steal_page(rxb); - skb_add_rx_frag(skb, 0, p, offset, len, len); + if (fraglen) { + int offset = (void *)hdr + hdrlen - rxb_addr(rxb); + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } iwl_update_stats(priv, false, fc, len); /* diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 8b1a7988e176..aa7aea168138 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -374,8 +374,9 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, if (WARN_ON(!rxb)) return; + rxcb.truesize = PAGE_SIZE << hw_params(trans).rx_page_order; dma_unmap_page(trans->dev, rxb->page_dma, - PAGE_SIZE << hw_params(trans).rx_page_order, + rxcb.truesize, DMA_FROM_DEVICE); rxcb._page = rxb->page; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 0c81cbaa8088..fdf97886a5e4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -260,6 +260,7 @@ static inline void iwl_free_resp(struct iwl_host_cmd *cmd) struct iwl_rx_cmd_buffer { struct page *_page; + unsigned int truesize; }; static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) -- cgit v1.2.3 From 4cb6e116bb97c8b87a1f4f95e99d0c8dda2a6e9b Mon Sep 17 00:00:00 2001 From: Ansis Atteka Date: Thu, 3 May 2012 18:40:38 -0700 Subject: openvswitch: Release rtnl_lock if ovs_vport_cmd_build_info() failed. This patch fixes a possible lock-up bug where rtnl_lock might not get released. Signed-off-by: Ansis Atteka Signed-off-by: Jesse Gross --- net/openvswitch/datapath.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index e44e631ea952..4cb615d46363 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1641,10 +1641,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq, OVS_VPORT_CMD_NEW); if (IS_ERR(reply)) { - err = PTR_ERR(reply); netlink_set_err(init_net.genl_sock, 0, - ovs_dp_vport_multicast_group.id, err); - return 0; + ovs_dp_vport_multicast_group.id, PTR_ERR(reply)); + goto exit_unlock; } genl_notify(reply, genl_info_net(info), info->snd_pid, -- cgit v1.2.3 From dad8a3b3eaa0c2ca25368a0b9f65edca84e27a40 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 23 Apr 2012 12:22:39 +0000 Subject: igb, ixgbe: netdev_tx_reset_queue incorrectly called from tx init path igb and ixgbe incorrectly call netdev_tx_reset_queue() from i{gb|xgbe}_clean_tx_ring() this sort of works in most cases except when the number of real tx queues changes. When the number of real tx queues changes netdev_tx_reset_queue() only gets called on the new number of queues so when we reduce the number of queues we risk triggering the watchdog timer and repeated device resets. So this is not only a cosmetic issue but causes real bugs. For example enabling/disabling DCB or FCoE in ixgbe will trigger this. CC: Alexander Duyck Signed-off-by: John Fastabend Tested-by: John Bishop Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 2 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 5ec31598ee47..d22350055285 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2771,8 +2771,6 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, txdctl |= E1000_TXDCTL_QUEUE_ENABLE; wr32(E1000_TXDCTL(reg_idx), txdctl); - - netdev_tx_reset_queue(txring_txq(ring)); } /** @@ -3282,6 +3280,8 @@ static void igb_clean_tx_ring(struct igb_ring *tx_ring) igb_unmap_and_free_tx_resource(tx_ring, buffer_info); } + netdev_tx_reset_queue(txring_txq(tx_ring)); + size = sizeof(struct igb_tx_buffer) * tx_ring->count; memset(tx_ring->tx_buffer_info, 0, size); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 31a2bf76a346..cfe7d269590c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1780,6 +1780,8 @@ static u16 ixgbe_clean_test_rings(struct ixgbe_ring *rx_ring, rx_desc = IXGBE_RX_DESC(rx_ring, rx_ntc); } + netdev_tx_reset_queue(txring_txq(tx_ring)); + /* re-map buffers to ring, store next to clean values */ ixgbe_alloc_rx_buffers(rx_ring, count); rx_ring->next_to_clean = rx_ntc; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 50f0700d250d..467948e9ecd9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2671,8 +2671,6 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, /* enable queue */ IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); - netdev_tx_reset_queue(txring_txq(ring)); - /* TXDCTL.EN will return 0 on 82598 if link is down, so skip it */ if (hw->mac.type == ixgbe_mac_82598EB && !(IXGBE_READ_REG(hw, IXGBE_LINKS) & IXGBE_LINKS_UP)) @@ -4167,6 +4165,8 @@ static void ixgbe_clean_tx_ring(struct ixgbe_ring *tx_ring) ixgbe_unmap_and_free_tx_resource(tx_ring, tx_buffer_info); } + netdev_tx_reset_queue(txring_txq(tx_ring)); + size = sizeof(struct ixgbe_tx_buffer) * tx_ring->count; memset(tx_ring->tx_buffer_info, 0, size); -- cgit v1.2.3 From dd7f5c9e5cb24f72140206d3e5961b4cc5a84034 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Sun, 25 Mar 2012 17:49:25 +0000 Subject: e1000: Silence sparse warnings by correcting type Silence sparse warnings shown below: ... drivers/net/ethernet/intel/e1000/e1000_main.c:3435:17: warning: cast to restricted __le64 drivers/net/ethernet/intel/e1000/e1000_main.c:3435:17: warning: cast to restricted __le64 ... Signed-off-by: Andrei Emeltchenko Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 4348b6fd44fa..37caa8885c2a 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -3380,7 +3380,7 @@ static void e1000_dump(struct e1000_adapter *adapter) for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) { struct e1000_tx_desc *tx_desc = E1000_TX_DESC(*tx_ring, i); struct e1000_buffer *buffer_info = &tx_ring->buffer_info[i]; - struct my_u { u64 a; u64 b; }; + struct my_u { __le64 a; __le64 b; }; struct my_u *u = (struct my_u *)tx_desc; const char *type; @@ -3424,7 +3424,7 @@ rx_ring_summary: for (i = 0; rx_ring->desc && (i < rx_ring->count); i++) { struct e1000_rx_desc *rx_desc = E1000_RX_DESC(*rx_ring, i); struct e1000_buffer *buffer_info = &rx_ring->buffer_info[i]; - struct my_u { u64 a; u64 b; }; + struct my_u { __le64 a; __le64 b; }; struct my_u *u = (struct my_u *)rx_desc; const char *type; -- cgit v1.2.3 From 24f06716cd5dbfc3d737ec2a24ac58ef76f68dc7 Mon Sep 17 00:00:00 2001 From: Ariel Elior Date: Sun, 6 May 2012 07:05:57 +0000 Subject: bnx2x: bug fix when loading after SAN boot This is a bug fix for an "interface fails to load" issue. The issue occurs when bnx2x driver loads after UNDI driver was previously loaded over the chip. In such a scenario the UNDI driver is loaded and operates in the pre-boot kernel, within its own specific host memory address range. When the pre-boot stage is complete, the real kernel is loaded, in a new and distinct host memory address range. The transition from pre-boot stage to boot is asynchronous from UNDI point of view. A race condition occurs when UNDI driver triggers a DMAE transaction to valid host addresses in the pre-boot stage, when control is diverted to the real kernel. This results in access to illegal addresses by our HW as the addresses which were valid in the preboot stage are no longer considered valid. Specifically, the 'was_error' bit in the pci glue of our device is set. This causes all following pci transactions from chip to host to timeout (in accordance to the pci spec). Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index e077d2508727..6af310195bae 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9122,13 +9122,34 @@ static int __devinit bnx2x_prev_unload_common(struct bnx2x *bp) return bnx2x_prev_mcp_done(bp); } +/* previous driver DMAE transaction may have occurred when pre-boot stage ended + * and boot began, or when kdump kernel was loaded. Either case would invalidate + * the addresses of the transaction, resulting in was-error bit set in the pci + * causing all hw-to-host pcie transactions to timeout. If this happened we want + * to clear the interrupt which detected this from the pglueb and the was done + * bit + */ +static void __devinit bnx2x_prev_interrupted_dmae(struct bnx2x *bp) +{ + u32 val = REG_RD(bp, PGLUE_B_REG_PGLUE_B_INT_STS); + if (val & PGLUE_B_PGLUE_B_INT_STS_REG_WAS_ERROR_ATTN) { + BNX2X_ERR("was error bit was found to be set in pglueb upon startup. Clearing"); + REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, 1 << BP_FUNC(bp)); + } +} + static int __devinit bnx2x_prev_unload(struct bnx2x *bp) { int time_counter = 10; u32 rc, fw, hw_lock_reg, hw_lock_val; BNX2X_DEV_INFO("Entering Previous Unload Flow\n"); - /* Release previously held locks */ + /* clear hw from errors which may have resulted from an interrupted + * dmae transaction. + */ + bnx2x_prev_interrupted_dmae(bp); + + /* Release previously held locks */ hw_lock_reg = (BP_FUNC(bp) <= 5) ? (MISC_REG_DRIVER_CONTROL_1 + BP_FUNC(bp) * 8) : (MISC_REG_DRIVER_CONTROL_7 + (BP_FUNC(bp) - 6) * 8); -- cgit v1.2.3 From 6eddcb4c82883451aec3be1240f17793370fa62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 26 Apr 2012 02:35:10 +0000 Subject: cdc_ether: Ignore bogus union descriptor for RNDIS devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some RNDIS devices include a bogus CDC Union descriptor pointing to non-existing interfaces. The RNDIS code is already prepared to handle devices without a CDC Union descriptor by hardwiring the driver to use interfaces 0 and 1, which is correct for the devices with the bogus descriptor as well. So we can reuse the existing workaround. Cc: Markus Kolb Cc: Iker Salmón San Millán Cc: Jonathan Nieder Cc: Oliver Neukum Cc: 655387@bugs.debian.org Cc: stable@vger.kernel.org Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ether.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 90a30026a931..00880edba048 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -83,6 +83,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) struct cdc_state *info = (void *) &dev->data; int status; int rndis; + bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); struct usb_cdc_mdlm_desc *desc = NULL; struct usb_cdc_mdlm_detail_desc *detail = NULL; @@ -195,6 +196,11 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) info->control, info->u->bSlaveInterface0, info->data); + /* fall back to hard-wiring for RNDIS */ + if (rndis) { + android_rndis_quirk = true; + goto next_desc; + } goto bad_desc; } if (info->control != intf) { @@ -271,11 +277,15 @@ next_desc: /* Microsoft ActiveSync based and some regular RNDIS devices lack the * CDC descriptors, so we'll hard-wire the interfaces and not check * for descriptors. + * + * Some Android RNDIS devices have a CDC Union descriptor pointing + * to non-existing interfaces. Ignore that and attempt the same + * hard-wired 0 and 1 interfaces. */ - if (rndis && !info->u) { + if (rndis && (!info->u || android_rndis_quirk)) { info->control = usb_ifnum_to_if(dev->udev, 0); info->data = usb_ifnum_to_if(dev->udev, 1); - if (!info->control || !info->data) { + if (!info->control || !info->data || info->control != intf) { dev_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n", info->control, -- cgit v1.2.3 From 1c430a727fa512500a422ffe4712166c550ea06a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 7 May 2012 15:39:06 +0200 Subject: net: compare_ether_addr[_64bits]() has no ordering Neither compare_ether_addr() nor compare_ether_addr_64bits() (as it can fall back to the former) have comparison semantics like memcmp() where the sign of the return value indicates sort order. We had a bug in the wireless code due to a blind memcmp replacement because of this. A cursory look suggests that the wireless bug was the only one due to this semantic difference. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 8a1835855faa..fe5136d81454 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -159,7 +159,8 @@ static inline void eth_hw_addr_random(struct net_device *dev) * @addr1: Pointer to a six-byte array containing the Ethernet address * @addr2: Pointer other six-byte array containing the Ethernet address * - * Compare two ethernet addresses, returns 0 if equal + * Compare two ethernet addresses, returns 0 if equal, non-zero otherwise. + * Unlike memcmp(), it doesn't return a value suitable for sorting. */ static inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2) { @@ -184,10 +185,10 @@ static inline unsigned long zap_last_2bytes(unsigned long value) * @addr1: Pointer to an array of 8 bytes * @addr2: Pointer to an other array of 8 bytes * - * Compare two ethernet addresses, returns 0 if equal. - * Same result than "memcmp(addr1, addr2, ETH_ALEN)" but without conditional - * branches, and possibly long word memory accesses on CPU allowing cheap - * unaligned memory reads. + * Compare two ethernet addresses, returns 0 if equal, non-zero otherwise. + * Unlike memcmp(), it doesn't return a value suitable for sorting. + * The function doesn't need any conditional branches and possibly uses + * word memory accesses on CPU allowing cheap unaligned memory reads. * arrays = { byte1, byte2, byte3, byte4, byte6, byte7, pad1, pad2} * * Please note that alignment of addr1 & addr2 is only guaranted to be 16 bits. -- cgit v1.2.3 From 072ae6314a191e3a9fc309b1e4e539ac7abc48ad Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Mon, 7 May 2012 17:21:53 -0700 Subject: openvswitch: Validation of IPv6 set port action uses IPv4 header When the kernel validates set TCP/UDP port actions, it looks at the ports in the existing flow to make sure that the L4 header exists. However, these actions always use the IPv4 version of the struct. Following patch fixes this by checking for flow ip protocol first. Signed-off-by: Pravin B Shelar Signed-off-by: Jesse Gross --- net/openvswitch/datapath.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 4cb615d46363..777716bc80f7 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -421,6 +421,19 @@ static int validate_sample(const struct nlattr *attr, return validate_actions(actions, key, depth + 1); } +static int validate_tp_port(const struct sw_flow_key *flow_key) +{ + if (flow_key->eth.type == htons(ETH_P_IP)) { + if (flow_key->ipv4.tp.src && flow_key->ipv4.tp.dst) + return 0; + } else if (flow_key->eth.type == htons(ETH_P_IPV6)) { + if (flow_key->ipv6.tp.src && flow_key->ipv6.tp.dst) + return 0; + } + + return -EINVAL; +} + static int validate_set(const struct nlattr *a, const struct sw_flow_key *flow_key) { @@ -462,18 +475,13 @@ static int validate_set(const struct nlattr *a, if (flow_key->ip.proto != IPPROTO_TCP) return -EINVAL; - if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst) - return -EINVAL; - - break; + return validate_tp_port(flow_key); case OVS_KEY_ATTR_UDP: if (flow_key->ip.proto != IPPROTO_UDP) return -EINVAL; - if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst) - return -EINVAL; - break; + return validate_tp_port(flow_key); default: return -EINVAL; -- cgit v1.2.3 From 3132d2827d92c2ee47fdf4dbec75bba0a2f291cb Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 5 May 2012 02:31:23 +0100 Subject: sfc: Fix division by zero when using one RX channel and no SR-IOV If RSS is disabled on the PF (efx->n_rx_channels == 1) we try to set up the indirection table so that VFs can use it, setting efx->rss_spread = efx_vf_size(efx). But if SR-IOV was disabled at compile time, this evaluates to 0 and we end up dividing by zero when initialising the table. I considered changing the fallback definition of efx_vf_size() to return 1, but its value is really meaningless if we are not going to enable VFs. Therefore add a condition of efx_sriov_wanted(efx) in efx_probe_interrupts(). Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/efx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 3cbfbffe3f00..4a0005342e65 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -1349,7 +1349,7 @@ static int efx_probe_interrupts(struct efx_nic *efx) } /* RSS might be usable on VFs even if it is disabled on the PF */ - efx->rss_spread = (efx->n_rx_channels > 1 ? + efx->rss_spread = ((efx->n_rx_channels > 1 || !efx_sriov_wanted(efx)) ? efx->n_rx_channels : efx_vf_size(efx)); return 0; -- cgit v1.2.3 From 477206a018f902895bfcd069dd820bfe94c187b1 Mon Sep 17 00:00:00 2001 From: Julien Ducourthial Date: Wed, 9 May 2012 00:00:06 +0200 Subject: r8169: fix unsigned int wraparound with TSO The r8169 may get stuck or show bad behaviour after activating TSO : the net_device is not stopped when it has no more TX descriptors. This problem comes from TX_BUFS_AVAIL which may reach -1 when all transmit descriptors are in use. The patch simply tries to keep positive values. Tested with 8111d(onboard) on a D510MO, and with 8111e(onboard) on a Zotac 890GXITX. Signed-off-by: Julien Ducourthial Acked-by: Francois Romieu Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index f54509377efa..ce6b44d1f252 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -61,8 +61,12 @@ #define R8169_MSG_DEFAULT \ (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN) -#define TX_BUFFS_AVAIL(tp) \ - (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1) +#define TX_SLOTS_AVAIL(tp) \ + (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx) + +/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */ +#define TX_FRAGS_READY_FOR(tp,nr_frags) \ + (TX_SLOTS_AVAIL(tp) >= (nr_frags + 1)) /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ @@ -5115,7 +5119,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, u32 opts[2]; int frags; - if (unlikely(TX_BUFFS_AVAIL(tp) < skb_shinfo(skb)->nr_frags)) { + if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) { netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n"); goto err_stop_0; } @@ -5169,7 +5173,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, mmiowb(); - if (TX_BUFFS_AVAIL(tp) < MAX_SKB_FRAGS) { + if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must * not miss a ring update when it notices a stopped queue. */ @@ -5183,7 +5187,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, * can't. */ smp_mb(); - if (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS) + if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) netif_wake_queue(dev); } @@ -5306,7 +5310,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) */ smp_mb(); if (netif_queue_stopped(dev) && - (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)) { + TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { netif_wake_queue(dev); } /* -- cgit v1.2.3 From dccd9ecc374462e5d6a5b8f8110415a86c2213d8 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 10 May 2012 22:16:32 -0400 Subject: ipv4: Do not use dead fib_info entries. Due to RCU lookups and RCU based release, fib_info objects can be found during lookup which have fi->fib_dead set. We must ignore these entries, otherwise we risk dereferencing the parts of the entry which are being torn down. Reported-by: Yevgen Pronenko Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index bce36f1a37b4..30b88d7b4bd6 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1370,6 +1370,8 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) continue; + if (fi->fib_dead) + continue; if (fa->fa_info->fib_scope < flp->flowi4_scope) continue; fib_alias_accessed(fa); -- cgit v1.2.3 From cfb8c3aa59302636c69890be10b2ef23a7ca83b2 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Thu, 10 May 2012 15:38:37 +0000 Subject: igb: fix rtnl race in PM resume path Since the caller (PM resume code) is not the one holding rtnl, when taking the 'else' branch rtnl may be released at any moment, thereby defeating the whole purpose of this code block. Signed-off-by: Benjamin Poirier Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/igb/igb_main.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index d22350055285..8683ca4748c8 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1111,9 +1111,12 @@ msi_only: adapter->flags |= IGB_FLAG_HAS_MSI; out: /* Notify the stack of the (possibly) reduced queue counts. */ + rtnl_lock(); netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); - return netif_set_real_num_rx_queues(adapter->netdev, - adapter->num_rx_queues); + err = netif_set_real_num_rx_queues(adapter->netdev, + adapter->num_rx_queues); + rtnl_unlock(); + return err; } /** @@ -6796,18 +6799,7 @@ static int igb_resume(struct device *dev) pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); - if (!rtnl_is_locked()) { - /* - * shut up ASSERT_RTNL() warning in - * netif_set_real_num_tx/rx_queues. - */ - rtnl_lock(); - err = igb_init_interrupt_scheme(adapter); - rtnl_unlock(); - } else { - err = igb_init_interrupt_scheme(adapter); - } - if (err) { + if (igb_init_interrupt_scheme(adapter)) { dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); return -ENOMEM; } -- cgit v1.2.3 From 380ec964bc19f865af70c0339dff1cb75dc4f8f2 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Thu, 10 May 2012 04:00:53 +0000 Subject: ehea: fix losing of NEQ events when one event occurred early The NEQ interrupt is only triggered when there was no previous pending interrupt. If we request irq handling after an interrupt has occurred, we will never get an interrupt until we call H_RESET_EVENTS. Events seem to be cleared when we first register the NEQ. So, when we requested irq handling right after registering it, a possible race with an interrupt was much less likely. Now, there is a chance we may lose this race and never get any events. The fix here is to poll and acknowledge any events that might have happened right after registering the irq handler. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ehea/ehea_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index c9069a28832b..f4d2da0db1b1 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3335,6 +3335,8 @@ static int __devinit ehea_probe_adapter(struct platform_device *dev, goto out_shutdown_ports; } + /* Handle any events that might be pending. */ + tasklet_hi_schedule(&adapter->neq_tasklet); ret = 0; goto out; -- cgit v1.2.3 From 59b9997baba5242997ddc7bd96b1391f5275a5a4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 10 May 2012 23:03:34 -0400 Subject: Revert "net: maintain namespace isolation between vlan and real device" This reverts commit 8a83a00b0735190384a348156837918271034144. It causes regressions for S390 devices, because it does an unconditional DST drop on SKBs for vlans and the QETH device needs the neighbour entry hung off the DST for certain things on transmit. Arnd can't remember exactly why he even needed this change. Conflicts: drivers/net/macvlan.c net/8021q/vlan_dev.c net/core/dev.c Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 2 +- include/linux/netdevice.h | 9 --------- net/8021q/vlan_dev.c | 2 +- net/core/dev.c | 36 +++++------------------------------- 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index f975afdc315c..025367a94add 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -259,7 +259,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) xmit_world: skb->ip_summed = ip_summed; - skb_set_dev(skb, vlan->lowerdev); + skb->dev = vlan->lowerdev; return dev_queue_xmit(skb); } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5cbaa20f1659..33900a53c990 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1403,15 +1403,6 @@ static inline bool netdev_uses_dsa_tags(struct net_device *dev) return 0; } -#ifndef CONFIG_NET_NS -static inline void skb_set_dev(struct sk_buff *skb, struct net_device *dev) -{ - skb->dev = dev; -} -#else /* CONFIG_NET_NS */ -void skb_set_dev(struct sk_buff *skb, struct net_device *dev); -#endif - static inline bool netdev_uses_trailer_tags(struct net_device *dev) { #ifdef CONFIG_NET_DSA_TAG_TRAILER diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 9988d4abb372..9757c193c86b 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -157,7 +157,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, skb = __vlan_hwaccel_put_tag(skb, vlan_tci); } - skb_set_dev(skb, vlan_dev_priv(dev)->real_dev); + skb->dev = vlan_dev_priv(dev)->real_dev; len = skb->len; if (netpoll_tx_running(dev)) return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); diff --git a/net/core/dev.c b/net/core/dev.c index 9bb8f87c4cda..99e1d759f41e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1617,10 +1617,14 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) return NET_RX_DROP; } skb->skb_iif = 0; - skb_set_dev(skb, dev); + skb->dev = dev; + skb_dst_drop(skb); skb->tstamp.tv64 = 0; skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, dev); + skb->mark = 0; + secpath_reset(skb); + nf_reset(skb); return netif_rx(skb); } EXPORT_SYMBOL_GPL(dev_forward_skb); @@ -1869,36 +1873,6 @@ void netif_device_attach(struct net_device *dev) } EXPORT_SYMBOL(netif_device_attach); -/** - * skb_dev_set -- assign a new device to a buffer - * @skb: buffer for the new device - * @dev: network device - * - * If an skb is owned by a device already, we have to reset - * all data private to the namespace a device belongs to - * before assigning it a new device. - */ -#ifdef CONFIG_NET_NS -void skb_set_dev(struct sk_buff *skb, struct net_device *dev) -{ - skb_dst_drop(skb); - if (skb->dev && !net_eq(dev_net(skb->dev), dev_net(dev))) { - secpath_reset(skb); - nf_reset(skb); - skb_init_secmark(skb); - skb->mark = 0; - skb->priority = 0; - skb->nf_trace = 0; - skb->ipvs_property = 0; -#ifdef CONFIG_NET_SCHED - skb->tc_index = 0; -#endif - } - skb->dev = dev; -} -EXPORT_SYMBOL(skb_set_dev); -#endif /* CONFIG_NET_NS */ - static void skb_warn_bad_offload(const struct sk_buff *skb) { static const netdev_features_t null_features = 0; -- cgit v1.2.3 From c57b54684060c8aced64a5b78ff69ff289af97b9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 May 2012 13:29:51 +0000 Subject: pktgen: fix crash at module unload commit 7d3d43dab4e9 (net: In unregister_netdevice_notifier unregister the netdevices.) makes pktgen crashing at module unload. [ 296.820578] BUG: spinlock bad magic on CPU#6, rmmod/3267 [ 296.820719] lock: ffff880310c38000, .magic: ffff8803, .owner: /-1, .owner_cpu: -1 [ 296.820943] Pid: 3267, comm: rmmod Not tainted 3.4.0-rc5+ #254 [ 296.821079] Call Trace: [ 296.821211] [] spin_dump+0x8a/0x8f [ 296.821345] [] spin_bug+0x21/0x26 [ 296.821507] [] do_raw_spin_lock+0x131/0x140 [ 296.821648] [] _raw_spin_lock+0x1e/0x20 [ 296.821786] [] __pktgen_NN_threads+0x4d/0x140 [pktgen] [ 296.821928] [] pktgen_device_event+0x10d/0x1e0 [pktgen] [ 296.822073] [] unregister_netdevice_notifier+0x7f/0x100 [ 296.822216] [] pg_cleanup+0x48/0x73 [pktgen] [ 296.822357] [] sys_delete_module+0x17e/0x2a0 [ 296.822502] [] system_call_fastpath+0x16/0x1b Hold the pktgen_thread_lock while splicing pktgen_threads, and test pktgen_exiting in pktgen_device_event() to make unload faster. Signed-off-by: Eric Dumazet Cc: Eric W. Biederman Signed-off-by: David S. Miller --- net/core/pktgen.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 4d8ce93cd503..77a59980b579 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1931,7 +1931,7 @@ static int pktgen_device_event(struct notifier_block *unused, { struct net_device *dev = ptr; - if (!net_eq(dev_net(dev), &init_net)) + if (!net_eq(dev_net(dev), &init_net) || pktgen_exiting) return NOTIFY_DONE; /* It is OK that we do not hold the group lock right now, @@ -3755,12 +3755,18 @@ static void __exit pg_cleanup(void) { struct pktgen_thread *t; struct list_head *q, *n; + struct list_head list; /* Stop all interfaces & threads */ pktgen_exiting = true; - list_for_each_safe(q, n, &pktgen_threads) { + mutex_lock(&pktgen_thread_lock); + list_splice(&list, &pktgen_threads); + mutex_unlock(&pktgen_thread_lock); + + list_for_each_safe(q, n, &list) { t = list_entry(q, struct pktgen_thread, th_list); + list_del(&t->th_list); kthread_stop(t->tsk); kfree(t); } -- cgit v1.2.3 From e0268868ba064980488fc8c194db3d8e9fb2959c Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 4 May 2012 05:24:54 +0000 Subject: sctp: check cached dst before using it dst_check() will take care of SA (and obsolete field), hence IPsec rekeying scenario is taken into account. Signed-off-by: Nicolas Dichtel Acked-by: Vlad Yaseivch Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 13 +++++++++++++ net/sctp/output.c | 4 +--- net/sctp/transport.c | 17 ----------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 6ee44b24864a..a2ef81466b00 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -704,4 +704,17 @@ static inline void sctp_v4_map_v6(union sctp_addr *addr) addr->v6.sin6_addr.s6_addr32[2] = htonl(0x0000ffff); } +/* The cookie is always 0 since this is how it's used in the + * pmtu code. + */ +static inline struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t) +{ + if (t->dst && !dst_check(t->dst, 0)) { + dst_release(t->dst); + t->dst = NULL; + } + + return t->dst; +} + #endif /* __net_sctp_h__ */ diff --git a/net/sctp/output.c b/net/sctp/output.c index 817174eb5f41..8fc4dcd294ab 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -377,9 +377,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) */ skb_set_owner_w(nskb, sk); - /* The 'obsolete' field of dst is set to 2 when a dst is freed. */ - if (!dst || (dst->obsolete > 1)) { - dst_release(dst); + if (!sctp_transport_dst_check(tp)) { sctp_transport_route(tp, NULL, sctp_sk(sk)); if (asoc && (asoc->param_flags & SPP_PMTUD_ENABLE)) { sctp_assoc_sync_pmtu(asoc); diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 3889330b7b04..b026ba0c6992 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -226,23 +226,6 @@ void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk) transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT; } -/* this is a complete rip-off from __sk_dst_check - * the cookie is always 0 since this is how it's used in the - * pmtu code - */ -static struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t) -{ - struct dst_entry *dst = t->dst; - - if (dst && dst->obsolete && dst->ops->check(dst, 0) == NULL) { - dst_release(t->dst); - t->dst = NULL; - return NULL; - } - - return dst; -} - void sctp_transport_update_pmtu(struct sctp_transport *t, u32 pmtu) { struct dst_entry *dst; -- cgit v1.2.3 From 38bf1953987c1735f3c9140fca762949a8cae507 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 4 May 2012 11:34:03 +0000 Subject: connector/userns: replace netlink uses of cap_raised() with capable() In 2009 Philip Reiser notied that a few users of netlink connector interface needed a capability check and added the idiom cap_raised(nsp->eff_cap, CAP_SYS_ADMIN) to a few of them, on the premise that netlink was asynchronous. In 2011 Patrick McHardy noticed we were being silly because netlink is synchronous and removed eff_cap from the netlink_skb_params and changed the idiom to cap_raised(current_cap(), CAP_SYS_ADMIN). Looking at those spots with a fresh eye we should be calling capable(CAP_SYS_ADMIN). The only reason I can see for not calling capable is that it once appeared we were not in the same task as the caller which would have made calling capable() impossible. In the initial user_namespace the only difference between between cap_raised(current_cap(), CAP_SYS_ADMIN) and capable(CAP_SYS_ADMIN) are a few sanity checks and the fact that capable(CAP_SYS_ADMIN) sets PF_SUPERPRIV if we use the capability. Since we are going to be using root privilege setting PF_SUPERPRIV seems the right thing to do. The motivation for this that patch is that in a child user namespace cap_raised(current_cap(),...) tests your capabilities with respect to that child user namespace not capabilities in the initial user namespace and thus will allow processes that should be unprivielged to use the kernel services that are only protected with cap_raised(current_cap(),..). To fix possible user_namespace issues and to just clean up the code replace cap_raised(current_cap(), CAP_SYS_ADMIN) with capable(CAP_SYS_ADMIN). Signed-off-by: Eric W. Biederman Cc: Patrick McHardy Cc: Philipp Reisner Acked-by: Serge E. Hallyn Acked-by: Andrew G. Morgan Cc: Vasiliy Kulikov Cc: David Howells Reviewed-by: James Morris Cc: David Miller Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- drivers/block/drbd/drbd_nl.c | 2 +- drivers/md/dm-log-userspace-transfer.c | 2 +- drivers/video/uvesafb.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index abfaacaaf346..946166e13953 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -2297,7 +2297,7 @@ static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms return; } - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) { + if (!capable(CAP_SYS_ADMIN)) { retcode = ERR_PERM; goto fail; } diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 1f23e048f077..08d9a207259a 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -134,7 +134,7 @@ static void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1); - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN)) return; spin_lock(&receiving_list_lock); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 26e83d7fdd6f..b0e2a4261afe 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -73,7 +73,7 @@ static void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *ns struct uvesafb_task *utask; struct uvesafb_ktask *task; - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN)) return; if (msg->seq >= UVESAFB_TASKS_MAX) -- cgit v1.2.3 From 13a8e0c8cdb43982372bd6c65fb26839c8fd8ce9 Mon Sep 17 00:00:00 2001 From: Jiri Bohac Date: Wed, 9 May 2012 01:01:40 +0000 Subject: bonding: don't increase rx_dropped after processing LACPDUs Since commit 3aba891d, bonding processes LACP frames (802.3ad mode) with bond_handle_frame(). Currently a copy of the skb is made and the original is left to be processed by other rx_handlers and the rest of the network stack by returning RX_HANDLER_ANOTHER. As there is no protocol handler for PKT_TYPE_LACPDU, the frame is dropped and dev->rx_dropped increased. Fix this by making bond_handle_frame() return RX_HANDLER_CONSUMED if bonding has processed the LACP frame. Signed-off-by: Jiri Bohac Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 18 ++++++++++++------ drivers/net/bonding/bond_3ad.h | 2 +- drivers/net/bonding/bond_main.c | 16 +++++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 793b00138275..3463b469e657 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2173,9 +2173,10 @@ re_arm: * received frames (loopback). Since only the payload is given to this * function, it check for loopback. */ -static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u16 length) +static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u16 length) { struct port *port; + int ret = RX_HANDLER_ANOTHER; if (length >= sizeof(struct lacpdu)) { @@ -2184,11 +2185,12 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u if (!port->slave) { pr_warning("%s: Warning: port of slave %s is uninitialized\n", slave->dev->name, slave->dev->master->name); - return; + return ret; } switch (lacpdu->subtype) { case AD_TYPE_LACPDU: + ret = RX_HANDLER_CONSUMED; pr_debug("Received LACPDU on port %d\n", port->actor_port_number); /* Protect against concurrent state machines */ @@ -2198,6 +2200,7 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u break; case AD_TYPE_MARKER: + ret = RX_HANDLER_CONSUMED; // No need to convert fields to Little Endian since we don't use the marker's fields. switch (((struct bond_marker *)lacpdu)->tlv_type) { @@ -2219,6 +2222,7 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u } } } + return ret; } /** @@ -2456,18 +2460,20 @@ out: return NETDEV_TX_OK; } -void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, +int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { + int ret = RX_HANDLER_ANOTHER; if (skb->protocol != PKT_TYPE_LACPDU) - return; + return ret; if (!pskb_may_pull(skb, sizeof(struct lacpdu))) - return; + return ret; read_lock(&bond->lock); - bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len); + ret = bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len); read_unlock(&bond->lock); + return ret; } /* diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h index 235b2cc58b28..5ee7e3c45db7 100644 --- a/drivers/net/bonding/bond_3ad.h +++ b/drivers/net/bonding/bond_3ad.h @@ -274,7 +274,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave); void bond_3ad_handle_link_change(struct slave *slave, char link); int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev); -void bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, +int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave); int bond_3ad_set_carrier(struct bonding *bond); void bond_3ad_update_lacp_rate(struct bonding *bond); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 62d2409bb293..bc13b3d77432 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1444,8 +1444,9 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) struct sk_buff *skb = *pskb; struct slave *slave; struct bonding *bond; - void (*recv_probe)(struct sk_buff *, struct bonding *, + int (*recv_probe)(struct sk_buff *, struct bonding *, struct slave *); + int ret = RX_HANDLER_ANOTHER; skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) @@ -1464,8 +1465,12 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); if (likely(nskb)) { - recv_probe(nskb, bond, slave); + ret = recv_probe(nskb, bond, slave); dev_kfree_skb(nskb); + if (ret == RX_HANDLER_CONSUMED) { + consume_skb(skb); + return ret; + } } } @@ -1487,7 +1492,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) memcpy(eth_hdr(skb)->h_dest, bond->dev->dev_addr, ETH_ALEN); } - return RX_HANDLER_ANOTHER; + return ret; } /* enslave device to bond device */ @@ -2723,7 +2728,7 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 } } -static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, +static int bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { struct arphdr *arp; @@ -2731,7 +2736,7 @@ static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, __be32 sip, tip; if (skb->protocol != __cpu_to_be16(ETH_P_ARP)) - return; + return RX_HANDLER_ANOTHER; read_lock(&bond->lock); @@ -2776,6 +2781,7 @@ static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, out_unlock: read_unlock(&bond->lock); + return RX_HANDLER_ANOTHER; } /* -- cgit v1.2.3 From c53cff5e42a06b81495983bd01741b9a954f11f0 Mon Sep 17 00:00:00 2001 From: Basil Gor Date: Thu, 3 May 2012 22:55:23 +0000 Subject: vhost-net: fix handle_rx buffer size Take vlan header length into account, when vlan id is stored as vlan_tci. Otherwise tagged packets coming from macvtap will be truncated. Signed-off-by: Basil Gor Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/vhost/net.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 1f21d2a1e528..5c170100de9c 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -283,8 +284,12 @@ static int peek_head_len(struct sock *sk) spin_lock_irqsave(&sk->sk_receive_queue.lock, flags); head = skb_peek(&sk->sk_receive_queue); - if (likely(head)) + if (likely(head)) { len = head->len; + if (vlan_tx_tag_present(head)) + len += VLAN_HLEN; + } + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags); return len; } -- cgit v1.2.3 From f09e2249c4f5c7c13261ec73f5a7807076af0c8e Mon Sep 17 00:00:00 2001 From: Basil Gor Date: Thu, 3 May 2012 22:55:24 +0000 Subject: macvtap: restore vlan header on user read Ethernet vlan header is not on the packet and kept in the skb->vlan_tci when it comes from lower dev. This patch inserts vlan header in user buffer during skb copy on user read. Signed-off-by: Basil Gor Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 0427c6561c84..cb8fd5069dbe 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -759,6 +760,8 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, struct macvlan_dev *vlan; int ret; int vnet_hdr_len = 0; + int vlan_offset = 0; + int copied; if (q->flags & IFF_VNET_HDR) { struct virtio_net_hdr vnet_hdr; @@ -773,18 +776,48 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr))) return -EFAULT; } + copied = vnet_hdr_len; + + if (!vlan_tx_tag_present(skb)) + len = min_t(int, skb->len, len); + else { + int copy; + struct { + __be16 h_vlan_proto; + __be16 h_vlan_TCI; + } veth; + veth.h_vlan_proto = htons(ETH_P_8021Q); + veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); + + vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); + len = min_t(int, skb->len + VLAN_HLEN, len); + + copy = min_t(int, vlan_offset, len); + ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy); + len -= copy; + copied += copy; + if (ret || !len) + goto done; + + copy = min_t(int, sizeof(veth), len); + ret = memcpy_toiovecend(iv, (void *)&veth, copied, copy); + len -= copy; + copied += copy; + if (ret || !len) + goto done; + } - len = min_t(int, skb->len, len); - - ret = skb_copy_datagram_const_iovec(skb, 0, iv, vnet_hdr_len, len); + ret = skb_copy_datagram_const_iovec(skb, vlan_offset, iv, copied, len); + copied += len; +done: rcu_read_lock_bh(); vlan = rcu_dereference_bh(q->vlan); if (vlan) - macvlan_count_rx(vlan, len, ret == 0, 0); + macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0); rcu_read_unlock_bh(); - return ret ? ret : (len + vnet_hdr_len); + return ret ? ret : copied; } static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, -- cgit v1.2.3 From 062e55e3960062fc2fb62a7274b4c253003eba73 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 10 May 2012 12:51:30 +0000 Subject: ks8851: Update link status during link change interrupt If a link change interrupt comes in we just clear the interrupt and continue along without notifying the upper networking layers that the link has changed. Use the mii_check_link() function to update the link status whenever a link change interrupt occurs. Cc: Ben Dooks Signed-off-by: Stephen Boyd Signed-off-by: David S. Miller --- drivers/net/ethernet/micrel/ks8851.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index f8dda009d3c0..5e313e9a252f 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -618,10 +618,8 @@ static void ks8851_irq_work(struct work_struct *work) netif_dbg(ks, intr, ks->netdev, "%s: status 0x%04x\n", __func__, status); - if (status & IRQ_LCI) { - /* should do something about checking link status */ + if (status & IRQ_LCI) handled |= IRQ_LCI; - } if (status & IRQ_LDI) { u16 pmecr = ks8851_rdreg16(ks, KS_PMECR); @@ -684,6 +682,9 @@ static void ks8851_irq_work(struct work_struct *work) mutex_unlock(&ks->lock); + if (status & IRQ_LCI) + mii_check_link(&ks->mii); + if (status & IRQ_TXI) netif_wake_queue(ks->netdev); -- cgit v1.2.3