From e1f8a19e6fc4f6d4267f6d3fe465553c3688f28e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 11 Jun 2009 12:08:15 +0200 Subject: sony: fix rfkill code again When the hard state changes, we shouldn't set the soft state to blocked as well -- we have no such indication from the device in that case so leave it untouched. Fixes http://bugzilla.kernel.org/show_bug.cgi?id=13458. Signed-off-by: Johannes Berg Reported-by: Reinette Chatre Tested-by: Reinette Chatre Signed-off-by: John W. Linville diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index e48d9a4..dafaa4a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1133,8 +1133,9 @@ static void sony_nc_rfkill_update() continue; if (hwblock) { - if (rfkill_set_hw_state(sony_rfkill_devices[i], true)) - sony_nc_rfkill_set((void *)i, true); + if (rfkill_set_hw_state(sony_rfkill_devices[i], true)) { + /* we already know we're blocked */ + } continue; } -- cgit v0.10.2 From 68f2d02669f7102be80aae47155f45e18950d223 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 11 Jun 2009 18:19:45 +0300 Subject: mac80211: Do not try to associate with an empty SSID It looks like some programs (e.g., NM) are setting an empty SSID with SIOCSIWESSID in some cases. This seems to trigger mac80211 to try to associate with an invalid configuration (wildcard SSID) which will result in failing associations (or odd issues, potentially including kernel panic with some drivers) if the AP were to actually accept this anyway). Only start association process if the SSID is actually set. This speeds up connection with NM in number of cases and avoids sending out broken association request frames. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d779c57..e5de9ce 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2445,6 +2445,14 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) ieee80211_set_disassoc(sdata, true, true, WLAN_REASON_DEAUTH_LEAVING); + if (ifmgd->ssid_len == 0) { + /* + * Only allow association to be started if a valid SSID + * is configured. + */ + return; + } + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) || ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); -- cgit v0.10.2 From b3781c74373489fa325ce64efdf72f6c8567b783 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Fri, 12 Jun 2009 12:45:36 -0700 Subject: libertas: fix IEEE PS mode in GSPI driver The card firmware does not set the Command Download Ready interrupt bit when IEEE PS mode is enabled, preventing the driver from sending commands (such as the command to exit IEEE PS mode) since there is no indication that the card is ready to accept commands. This patch works around the problem by using the the TX Download Ready bit in place of the Command Download Ready Bit while in IEEE PS mode. TX Download Ready is set in IEEE PS mode. Signed-off-by: Andrey Yurovsky Signed-off-by: Javier Cardona Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index f8c2898..dc2a1f6 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -875,7 +875,12 @@ static int lbs_spi_thread(void *data) err = if_spi_c2h_data(card); if (err) goto err; - if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY) { + + /* workaround: in PS mode, the card does not set the Command + * Download Ready bit, but it sets TX Download Ready. */ + if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || + (card->priv->psstate != PS_STATE_FULL_POWER && + (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { /* This means two things. First of all, * if there was a previous command sent, the card has * successfully received it. -- cgit v0.10.2 From 87e501b351ed9d837e63f6506b7e66331a69ed58 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 12 Jun 2009 21:37:31 +0200 Subject: net/libertas: don't recursive spin lock in if_spi_e2h() |BUG: spinlock recursion on CPU#0, lbs_spi_thread/1030 | lock: dee9a1bc, .magic: dead4ead, .owner: lbs_spi_thread/1030, .owner_cpu: 0 |Call Trace: |[deec3eb0] [c0007220] show_stack+0x4c/0x15c (unreliable) |[deec3ef0] [c0187720] spin_bug+0x9c/0xb0 |[deec3f10] [c0187890] _raw_spin_lock+0x54/0x148 |[deec3f40] [c02d260c] _spin_lock_irqsave+0x2c/0x44 |[deec3f60] [e8671800] lbs_queue_event+0x7c/0x130 [libertas] |[deec3f80] [e8725f04] lbs_spi_thread+0x538/0x58c [libertas_spi] |[deec3fe0] [c004f270] kthread+0x4c/0x88 |[deec3ff0] [c000f33c] kernel_thread+0x4c/0x68 if_spi_e2h() is grabbing ->driver_lock just while calling lbs_queue_event() which is grabbing the same lock. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index dc2a1f6..923ed58 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -812,7 +812,6 @@ out: static void if_spi_e2h(struct if_spi_card *card) { int err = 0; - unsigned long flags; u32 cause; struct lbs_private *priv = card->priv; @@ -827,10 +826,7 @@ static void if_spi_e2h(struct if_spi_card *card) /* generate a card interrupt */ spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); - spin_lock_irqsave(&priv->driver_lock, flags); lbs_queue_event(priv, cause & 0xff); - spin_unlock_irqrestore(&priv->driver_lock, flags); - out: if (err) lbs_pr_err("%s: error %d\n", __func__, err); -- cgit v0.10.2 From d7129e190c3107db8ac11a9e51035641b68d2aa7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 12 Jun 2009 13:22:48 -0700 Subject: iwlwifi: revamp bss_info_changed My earlier patch, "mac80211: unify config_interface and bss_info_changed" introduced a bug in iwlwifi where it will do some things incorrectly now when reassociating. Revamp iwl_bss_info_changed to fix that issue and make it easier to read. Also, while at it, add comments about things that it should do but currently doesn't. Finally, also improve the locking in the function. Signed-off-by: Johannes Berg Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index f9d16ca..e05faa0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2390,39 +2390,46 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, priv->ibss_beacon = ieee80211_beacon_get(hw, vif); } - if ((changes & BSS_CHANGED_BSSID) && !iwl_is_rfkill(priv)) { - /* If there is currently a HW scan going on in the background - * then we need to cancel it else the RXON below will fail. */ + if (changes & BSS_CHANGED_BEACON_INT) { + priv->beacon_int = bss_conf->beacon_int; + /* TODO: in AP mode, do something to make this take effect */ + } + + if (changes & BSS_CHANGED_BSSID) { + IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid); + + /* + * If there is currently a HW scan going on in the + * background then we need to cancel it else the RXON + * below/in post_associate will fail. + */ if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARN(priv, "Aborted scan still in progress " - "after 100ms\n"); + IWL_WARN(priv, "Aborted scan still in progress after 100ms\n"); IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); mutex_unlock(&priv->mutex); return; } - memcpy(priv->staging_rxon.bssid_addr, - bss_conf->bssid, ETH_ALEN); - /* TODO: Audit driver for usage of these members and see - * if mac80211 deprecates them (priv->bssid looks like it - * shouldn't be there, but I haven't scanned the IBSS code - * to verify) - jpk */ - memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); + /* mac80211 only sets assoc when in STATION mode */ + if (priv->iw_mode == NL80211_IFTYPE_ADHOC || + bss_conf->assoc) { + memcpy(priv->staging_rxon.bssid_addr, + bss_conf->bssid, ETH_ALEN); - if (priv->iw_mode == NL80211_IFTYPE_AP) - iwlcore_config_ap(priv); - else { - int rc = iwlcore_commit_rxon(priv); - if ((priv->iw_mode == NL80211_IFTYPE_STATION) && rc) - iwl_rxon_add_station( - priv, priv->active_rxon.bssid_addr, 1); + /* currently needed in a few places */ + memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); + } else { + priv->staging_rxon.filter_flags &= + ~RXON_FILTER_ASSOC_MSK; } - } else if (!iwl_is_rfkill(priv)) { - iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv); + } + /* + * This needs to be after setting the BSSID in case + * mac80211 decides to do both changes at once because + * it will invoke post_associate. + */ if (priv->iw_mode == NL80211_IFTYPE_ADHOC && changes & BSS_CHANGED_BEACON) { struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); @@ -2431,8 +2438,6 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, iwl_mac_beacon_update(hw, beacon); } - mutex_unlock(&priv->mutex); - if (changes & BSS_CHANGED_ERP_PREAMBLE) { IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); @@ -2450,6 +2455,23 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, priv->staging_rxon.flags &= ~RXON_FLG_TGG_PROTECT_MSK; } + if (changes & BSS_CHANGED_BASIC_RATES) { + /* XXX use this information + * + * To do that, remove code from iwl_set_rate() and put something + * like this here: + * + if (A-band) + priv->staging_rxon.ofdm_basic_rates = + bss_conf->basic_rates; + else + priv->staging_rxon.ofdm_basic_rates = + bss_conf->basic_rates >> 4; + priv->staging_rxon.cck_basic_rates = + bss_conf->basic_rates & 0xF; + */ + } + if (changes & BSS_CHANGED_HT) { iwl_ht_conf(priv, bss_conf); @@ -2459,10 +2481,6 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_ASSOC) { IWL_DEBUG_MAC80211(priv, "ASSOC %d\n", bss_conf->assoc); - /* This should never happen as this function should - * never be called from interrupt context. */ - if (WARN_ON_ONCE(in_interrupt())) - return; if (bss_conf->assoc) { priv->assoc_id = bss_conf->aid; priv->beacon_int = bss_conf->beacon_int; @@ -2470,27 +2488,35 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, priv->timestamp = bss_conf->timestamp; priv->assoc_capability = bss_conf->assoc_capability; - /* we have just associated, don't start scan too early - * leave time for EAPOL exchange to complete + /* + * We have just associated, don't start scan too early + * leave time for EAPOL exchange to complete. + * + * XXX: do this in mac80211 */ priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN_AFTER_ASSOC; - mutex_lock(&priv->mutex); - priv->cfg->ops->lib->post_associate(priv); - mutex_unlock(&priv->mutex); - } else { + if (!iwl_is_rfkill(priv)) + priv->cfg->ops->lib->post_associate(priv); + } else priv->assoc_id = 0; - IWL_DEBUG_MAC80211(priv, "DISASSOC %d\n", bss_conf->assoc); + + } + + if (changes && iwl_is_associated(priv) && priv->assoc_id) { + IWL_DEBUG_MAC80211(priv, "Changes (%#x) while associated\n", + changes); + ret = iwl_send_rxon_assoc(priv); + if (!ret) { + /* Sync active_rxon with latest change. */ + memcpy((void *)&priv->active_rxon, + &priv->staging_rxon, + sizeof(struct iwl_rxon_cmd)); } - } else if (changes && iwl_is_associated(priv) && priv->assoc_id) { - IWL_DEBUG_MAC80211(priv, "Associated Changes %d\n", changes); - ret = iwl_send_rxon_assoc(priv); - if (!ret) - /* Sync active_rxon with latest change. */ - memcpy((void *)&priv->active_rxon, - &priv->staging_rxon, - sizeof(struct iwl_rxon_cmd)); } + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); } EXPORT_SYMBOL(iwl_bss_info_changed); -- cgit v0.10.2 From 8e8df3a08c6f3098b54639ee6892e3753ad5f210 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 12 Jun 2009 13:22:49 -0700 Subject: iwlwifi: checking for 40MHz mode when checking for 40MHz, compare ht_protection to IEEE80211_HT_OP_MODE_PROTECTION_20MHZ. ht_protection is not a bit-mask field Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index e05faa0..b2875fe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -630,7 +630,7 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, return 0; } - if (iwl_ht_conf->ht_protection & IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) + if (iwl_ht_conf->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) return 1; else return iwl_is_channel_extension(priv, priv->band, @@ -826,7 +826,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); if (iwl_is_fat_tx_allowed(priv, NULL)) { /* pure 40 fat */ - if (rxon->flags & RXON_FLG_FAT_PROT_MSK) + if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; else { /* Note: control channel is opposite of extension channel */ -- cgit v0.10.2 From 508b08e7121f2083c9e9cd82f10b9088b6bc13fb Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 12 Jun 2009 13:22:50 -0700 Subject: iwlwifi: check control channel for pure 40MHz for pure 40MHz mode, set the control channel location if provided, but not like Mixed mode; if information is not provided, still allow 40MHz operation. Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index b2875fe..b122eed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -826,9 +826,18 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); if (iwl_is_fat_tx_allowed(priv, NULL)) { /* pure 40 fat */ - if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) + if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; - else { + /* Note: control channel is opposite of extension channel */ + switch (ht_info->extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + } + } else { /* Note: control channel is opposite of extension channel */ switch (ht_info->extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: -- cgit v0.10.2 From 611d3eb72aa7847a1081e6c1ac05fd2012652cde Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 12 Jun 2009 13:22:51 -0700 Subject: iwlwifi: check for channel location for 40MHz for both mixed and pure 40MHz, need to check for valid channel location. if the specified channel not allow the channel location requested (ABOVE, BELOW), then reject the Fat channel access This fixes http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=1988 ("iwlwifi: checking for 40MHz mode" and "iwlwifi: check control channel for pure 40MHz" combine with this to address the above bug. -- JWL) Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index b122eed..6ab0716 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -629,13 +629,9 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, if (!sta_ht_inf->ht_supported) return 0; } - - if (iwl_ht_conf->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) - return 1; - else - return iwl_is_channel_extension(priv, priv->band, - le16_to_cpu(priv->staging_rxon.channel), - iwl_ht_conf->extension_chan_offset); + return iwl_is_channel_extension(priv, priv->band, + le16_to_cpu(priv->staging_rxon.channel), + iwl_ht_conf->extension_chan_offset); } EXPORT_SYMBOL(iwl_is_fat_tx_allowed); -- cgit v0.10.2 From 8a9b99267cb3b51d4e59693c03e1204d86b42445 Mon Sep 17 00:00:00 2001 From: Abhijeet Kolekar Date: Fri, 12 Jun 2009 13:22:52 -0700 Subject: iwlwifi/iwl3945: fix suspend resume association bug Patch fixes the following bugs at http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=2005 http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=2007 If we suspend with an association and then resumed, we need to synchronize the active rxon with staging rxon, else we will get an error when iwl_alive_start try to commit rxon and staging is set to channel 0. Before going to suspend staging and active rxon are in sync. After resuming from the suspend, iwl_mac_start is called and it clears the staging rxon. Patch fixes the bug by not clearing the staging rxon in iwl_mac_start. Patch also adds similar fix to 3945. Signed-off-by: Abhijeet Kolekar Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index a5637c4..6d1519e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2152,7 +2152,6 @@ static int iwl_mac_start(struct ieee80211_hw *hw) /* we should be verifying the device is ready to be opened */ mutex_lock(&priv->mutex); - memset(&priv->staging_rxon, 0, sizeof(struct iwl_rxon_cmd)); /* fetch ucode file from disk, alloc and copy to bus-master buffers ... * ucode filename and max sizes are card-specific. */ diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 83d3160..cb9bd4c 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2498,8 +2498,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv) struct iwl3945_rxon_cmd *active_rxon = (struct iwl3945_rxon_cmd *)(&priv->active_rxon); - memcpy(&priv->staging_rxon, &priv->active_rxon, - sizeof(priv->staging_rxon)); + priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { /* Initialize our rx_config data */ @@ -3147,7 +3146,6 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw) /* we should be verifying the device is ready to be opened */ mutex_lock(&priv->mutex); - memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon)); /* fetch ucode file from disk, alloc and copy to bus-master buffers ... * ucode filename and max sizes are card-specific. */ -- cgit v0.10.2 From 35edf8aae8f903b154d658b9a7eed0d5c1a4a814 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Fri, 12 Jun 2009 16:09:53 -0700 Subject: ath5k: fix mesh beaconing This patch is from Nick Kossifidis but he forgot to send it. It ensures that the beacon queue gets started in mesh mode as well, otherwise ath5k will not beacon in mesh point mode. At this time, we still need to issue a scan before mesh beaconing will work but that appears to be a separate problem. Signed-off-by: Andrey Yurovsky Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index ec35503..2942f13 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -733,8 +733,9 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) /* * Set the beacon register and enable all timers. */ - /* When in AP mode zero timer0 to start TSF */ - if (ah->ah_op_mode == NL80211_IFTYPE_AP) + /* When in AP or Mesh Point mode zero timer0 to start TSF */ + if (ah->ah_op_mode == NL80211_IFTYPE_AP || + ah->ah_op_mode == NL80211_IFTYPE_MESH_POINT) ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); -- cgit v0.10.2 From 82880a7cf9c059c11cd6ad1868c48969956f966b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 13 Jun 2009 14:50:24 +0530 Subject: ath9k: Add helper to get ath9k specific current channel Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index f7baa40..89bf07e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -231,6 +231,19 @@ static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) } } +static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc, + struct ieee80211_hw *hw) +{ + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *channel; + u8 chan_idx; + + chan_idx = curchan->hw_value; + channel = &sc->sc_ah->channels[chan_idx]; + ath9k_update_ichannel(sc, hw, channel); + return channel; +} + /* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending @@ -1920,7 +1933,7 @@ static int ath9k_start(struct ieee80211_hw *hw) struct ath_softc *sc = aphy->sc; struct ieee80211_channel *curchan = hw->conf.channel; struct ath9k_channel *init_channel; - int r, pos; + int r; DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with " "initial channel: %d MHz\n", curchan->center_freq); @@ -1950,11 +1963,9 @@ static int ath9k_start(struct ieee80211_hw *hw) /* setup initial channel */ - pos = curchan->hw_value; + sc->chan_idx = curchan->hw_value; - sc->chan_idx = pos; - init_channel = &sc->sc_ah->channels[pos]; - ath9k_update_ichannel(sc, hw, init_channel); + init_channel = ath_get_curchannel(sc, hw); /* Reset SERDES registers */ ath9k_hw_configpcipowersave(sc->sc_ah, 0); -- cgit v0.10.2 From 159cd468bc885f7a2fbc75bbfe782782e52ef9c9 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 13 Jun 2009 14:50:25 +0530 Subject: ath9k: Make sure we have current channel in ah_curchan before rf disable/enable Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 89bf07e..6516a42 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1123,6 +1123,9 @@ void ath_radio_enable(struct ath_softc *sc) ath9k_ps_wakeup(sc); ath9k_hw_configpcipowersave(ah, 0); + if (!ah->curchan) + ah->curchan = ath_get_curchannel(sc, sc->hw); + spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, ah->curchan, false); if (r) { @@ -1175,6 +1178,9 @@ void ath_radio_disable(struct ath_softc *sc) ath_stoprecv(sc); /* turn off frame recv */ ath_flushrecv(sc); /* flush recv queue */ + if (!ah->curchan) + ah->curchan = ath_get_curchannel(sc, sc->hw); + spin_lock_bh(&sc->sc_resetlock); r = ath9k_hw_reset(ah, ah->curchan, false); if (r) { -- cgit v0.10.2 From 3b319aae4244f9b4758212605f67cf63207a4fa1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 13 Jun 2009 14:50:26 +0530 Subject: ath9k: port to cfg80211 rfkill This ports the ath9k rfkill code to the new API offered by cfg80211 and thus removes a lot of useless stuff. ("With this series a kernel panic, which is a regression, during module unload disappears." -- Vasanthakumar Thiagarajan Other patches in the series: ath9k: Add helper to get ath9k specific current channel ath9k: Make sure we have current channel in ah_curchan before rf disable/enable -- JWL) Signed-off-by: Johannes Berg Cc: Luis Rodriguez Tested-by: Vasanthakumar Thiagarajan Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 0ed1ac3..2d79610 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -1,7 +1,6 @@ config ATH9K tristate "Atheros 802.11n wireless cards support" depends on PCI && MAC80211 && WLAN_80211 - depends on RFKILL || RFKILL=n select ATH_COMMON select MAC80211_LEDS select LEDS_CLASS diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 515880a..a6e65ff 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -21,7 +21,6 @@ #include #include #include -#include #include "hw.h" #include "rc.h" @@ -460,12 +459,6 @@ struct ath_led { bool registered; }; -struct ath_rfkill { - struct rfkill *rfkill; - struct rfkill_ops ops; - char rfkill_name[32]; -}; - /********************/ /* Main driver core */ /********************/ @@ -505,7 +498,6 @@ struct ath_rfkill { #define SC_OP_PROTECT_ENABLE BIT(6) #define SC_OP_RXFLUSH BIT(7) #define SC_OP_LED_ASSOCIATED BIT(8) -#define SC_OP_RFKILL_REGISTERED BIT(9) #define SC_OP_WAIT_FOR_BEACON BIT(12) #define SC_OP_LED_ON BIT(13) #define SC_OP_SCANNING BIT(14) @@ -591,7 +583,6 @@ struct ath_softc { int beacon_interval; - struct ath_rfkill rf_kill; struct ath_ani ani; struct ath9k_node_stats nodestats; #ifdef CONFIG_ATH9K_DEBUG diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1579c94..34935a8 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2186,6 +2186,18 @@ static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); } +static void ath9k_enable_rfkill(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); + + REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, + AR_GPIO_INPUT_MUX2_RFSILENT); + + ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio); + REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); +} + int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange) { @@ -2313,10 +2325,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_interrupt_masks(ah, ah->opmode); ath9k_hw_init_qos(ah); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) ath9k_enable_rfkill(ah); -#endif + ath9k_hw_init_user_settings(ah); REG_WRITE(ah, AR_STA_ID1, @@ -3613,20 +3624,6 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) AR_GPIO_BIT(gpio)); } -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) -void ath9k_enable_rfkill(struct ath_hw *ah) -{ - REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, - AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); - - REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, - AR_GPIO_INPUT_MUX2_RFSILENT); - - ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio); - REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); -} -#endif - u32 ath9k_hw_getdefantenna(struct ath_hw *ah) { return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index dd8508e..9d0b31a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -565,9 +565,6 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) -void ath9k_enable_rfkill(struct ath_hw *ah); -#endif u32 ath9k_hw_getdefantenna(struct ath_hw *ah); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); bool ath9k_hw_setantennaswitch(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6516a42..91c0048 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1197,8 +1197,6 @@ void ath_radio_disable(struct ath_softc *sc) ath9k_ps_restore(sc); } -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - /*******************/ /* Rfkill */ /*******************/ @@ -1211,81 +1209,27 @@ static bool ath_is_rfkill_set(struct ath_softc *sc) ah->rfkill_polarity; } -/* s/w rfkill handlers */ -static int ath_rfkill_set_block(void *data, bool blocked) -{ - struct ath_softc *sc = data; - - if (blocked) - ath_radio_disable(sc); - else - ath_radio_enable(sc); - - return 0; -} - -static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data) +static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) { - struct ath_softc *sc = data; + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; bool blocked = !!ath_is_rfkill_set(sc); - if (rfkill_set_hw_state(rfkill, blocked)) + wiphy_rfkill_set_hw_state(hw->wiphy, blocked); + + if (blocked) ath_radio_disable(sc); else ath_radio_enable(sc); } -/* Init s/w rfkill */ -static int ath_init_sw_rfkill(struct ath_softc *sc) +static void ath_start_rfkill_poll(struct ath_softc *sc) { - sc->rf_kill.ops.set_block = ath_rfkill_set_block; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - sc->rf_kill.ops.poll = ath_rfkill_poll_state; - - snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), - "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); - - sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name, - wiphy_dev(sc->hw->wiphy), - RFKILL_TYPE_WLAN, - &sc->rf_kill.ops, sc); - if (!sc->rf_kill.rfkill) { - DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); - return -ENOMEM; - } - - return 0; -} - -/* Deinitialize rfkill */ -static void ath_deinit_rfkill(struct ath_softc *sc) -{ - if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { - rfkill_unregister(sc->rf_kill.rfkill); - rfkill_destroy(sc->rf_kill.rfkill); - sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; - } -} - -static int ath_start_rfkill_poll(struct ath_softc *sc) -{ - if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { - if (rfkill_register(sc->rf_kill.rfkill)) { - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to register rfkill\n"); - rfkill_destroy(sc->rf_kill.rfkill); - - /* Deinitialize the device */ - ath_cleanup(sc); - return -EIO; - } else { - sc->sc_flags |= SC_OP_RFKILL_REGISTERED; - } - } + struct ath_hw *ah = sc->sc_ah; - return 0; + if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + wiphy_rfkill_start_polling(sc->hw->wiphy); } -#endif /* CONFIG_RFKILL */ void ath_cleanup(struct ath_softc *sc) { @@ -1305,9 +1249,6 @@ void ath_detach(struct ath_softc *sc) DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - ath_deinit_rfkill(sc); -#endif ath_deinit_leds(sc); cancel_work_sync(&sc->chan_work); cancel_delayed_work_sync(&sc->wiphy_work); @@ -1645,13 +1586,6 @@ int ath_attach(u16 devid, struct ath_softc *sc) if (error != 0) goto error_attach; -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - /* Initialize s/w rfkill */ - error = ath_init_sw_rfkill(sc); - if (error) - goto error_attach; -#endif - INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); sc->wiphy_scheduler_int = msecs_to_jiffies(500); @@ -1667,6 +1601,7 @@ int ath_attach(u16 devid, struct ath_softc *sc) /* Initialize LED control */ ath_init_leds(sc); + ath_start_rfkill_poll(sc); return 0; @@ -2035,10 +1970,6 @@ static int ath9k_start(struct ieee80211_hw *hw) ieee80211_wake_queues(hw); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - r = ath_start_rfkill_poll(sc); -#endif - mutex_unlock: mutex_unlock(&sc->mutex); @@ -2176,7 +2107,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) } else sc->rx.rxlink = NULL; - rfkill_pause_polling(sc->rf_kill.rfkill); + wiphy_rfkill_stop_polling(sc->hw->wiphy); /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); @@ -2782,6 +2713,7 @@ struct ieee80211_ops ath9k_ops = { .ampdu_action = ath9k_ampdu_action, .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, + .rfkill_poll = ath9k_rfkill_poll_state, }; static struct { -- cgit v0.10.2 From db2e6bd4e966a36c6b2f1921feb3537e8254415c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 14 Jun 2009 17:37:39 +0200 Subject: mac80211: add queue debugfs file I suspect that some driver bugs can cause queues to be stopped while they shouldn't be, but it's hard to find out whether that is the case or not without having any visible information about the queues. This adds a file to debugfs that allows us to see the queues' statuses. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 11c7231..6c439cd 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -163,6 +163,29 @@ static const struct file_operations noack_ops = { .open = mac80211_open_file_generic }; +static ssize_t queues_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + unsigned long flags; + char buf[IEEE80211_MAX_QUEUES * 20]; + int q, res = 0; + + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + for (q = 0; q < local->hw.queues; q++) + res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q, + local->queue_stop_reasons[q], + __netif_subqueue_stopped(local->mdev, q)); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static const struct file_operations queues_ops = { + .read = queues_read, + .open = mac80211_open_file_generic +}; + /* statistics stuff */ #define DEBUGFS_STATS_FILE(name, buflen, fmt, value...) \ @@ -298,6 +321,7 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(tsf); + DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD(noack); @@ -350,6 +374,7 @@ void debugfs_hw_del(struct ieee80211_local *local) DEBUGFS_DEL(total_ps_buffered); DEBUGFS_DEL(wep_iv); DEBUGFS_DEL(tsf); + DEBUGFS_DEL(queues); DEBUGFS_DEL(reset); DEBUGFS_DEL(noack); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4dbc289..f41fe1f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -783,6 +783,7 @@ struct ieee80211_local { struct dentry *total_ps_buffered; struct dentry *wep_iv; struct dentry *tsf; + struct dentry *queues; struct dentry *reset; struct dentry *noack; struct dentry *statistics; -- cgit v0.10.2 From 7e9debe9789456426ec8574ead879e33da19ee57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 15 Jun 2009 13:42:25 +0200 Subject: mac80211: disconnect when user changes channel If we do not disconnect when a channel switch is requested, we end up eventually detection beacon loss from the AP and then disconnecting, without ever really telling the AP, so we might just as well disconnect right away. Additionally, this fixes a problem with iwlwifi where the driver will clear some internal state on channel changes like this and then get confused when we actually go clear that state from mac80211. It may look like this patch drops the no-IBSS check, but that is already handled by cfg80211 in the wext handler it provides for IBSS (cfg80211_ibss_wext_siwfreq). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f41fe1f..68eb505 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1101,7 +1101,6 @@ void ieee802_11_parse_elems(u8 *start, size_t len, u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, struct ieee802_11_elems *elems, u64 filter, u32 crc); -int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq); u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e5de9ce..84e59b9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2223,7 +2223,10 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) capa_mask, capa_val); if (bss) { - ieee80211_set_freq(sdata, bss->cbss.channel->center_freq); + local->oper_channel = bss->cbss.channel; + local->oper_channel_type = NL80211_CHAN_NO_HT; + ieee80211_hw_config(local, 0); + if (!(ifmgd->flags & IEEE80211_STA_SSID_SET)) ieee80211_sta_set_ssid(sdata, bss->ssid, bss->ssid_len); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 66ce96a..915e777 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -774,31 +774,6 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, dev_queue_xmit(skb); } -int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz) -{ - int ret = -EINVAL; - struct ieee80211_channel *chan; - struct ieee80211_local *local = sdata->local; - - chan = ieee80211_get_channel(local->hw.wiphy, freqMHz); - - if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - if (sdata->vif.type == NL80211_IFTYPE_ADHOC && - chan->flags & IEEE80211_CHAN_NO_IBSS) - return ret; - local->oper_channel = chan; - local->oper_channel_type = NL80211_CHAN_NO_HT; - - if (local->sw_scanning || local->hw_scanning) - ret = 0; - else - ret = ieee80211_hw_config( - local, IEEE80211_CONF_CHANGE_CHANNEL); - } - - return ret; -} - u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band) { diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index d2d81b1..1da81f4 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -55,6 +55,8 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_freq *freq, char *extra) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_channel *chan; if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); @@ -69,17 +71,38 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, IEEE80211_STA_AUTO_CHANNEL_SEL; return 0; } else - return ieee80211_set_freq(sdata, + chan = ieee80211_get_channel(local->hw.wiphy, ieee80211_channel_to_frequency(freq->m)); } else { int i, div = 1000000; for (i = 0; i < freq->e; i++) div /= 10; - if (div > 0) - return ieee80211_set_freq(sdata, freq->m / div); - else + if (div <= 0) return -EINVAL; + chan = ieee80211_get_channel(local->hw.wiphy, freq->m / div); } + + if (!chan) + return -EINVAL; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + + /* + * no change except maybe auto -> fixed, ignore the HT + * setting so you can fix a channel you're on already + */ + if (local->oper_channel == chan) + return 0; + + if (sdata->vif.type == NL80211_IFTYPE_STATION) + ieee80211_sta_req_auth(sdata); + + local->oper_channel = chan; + local->oper_channel_type = NL80211_CHAN_NO_HT; + ieee80211_hw_config(local, 0); + + return 0; } -- cgit v0.10.2 From ce0879e324df007f12cc25227102847b92fcafb7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 15 Jun 2009 15:36:38 +0200 Subject: rfkill: improve docs Now that the dust has settled a bit, improve the docs on rfkill and include more information about /dev/rfkill. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index 1b74b5f..c8acd86 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -3,9 +3,8 @@ rfkill - RF kill switch support 1. Introduction 2. Implementation details -3. Kernel driver guidelines -4. Kernel API -5. Userspace support +3. Kernel API +4. Userspace support 1. Introduction @@ -19,82 +18,62 @@ disable all transmitters of a certain type (or all). This is intended for situations where transmitters need to be turned off, for example on aircraft. +The rfkill subsystem has a concept of "hard" and "soft" block, which +differ little in their meaning (block == transmitters off) but rather in +whether they can be changed or not: + - hard block: read-only radio block that cannot be overriden by software + - soft block: writable radio block (need not be readable) that is set by + the system software. 2. Implementation details -The rfkill subsystem is composed of various components: the rfkill class, the -rfkill-input module (an input layer handler), and some specific input layer -events. - -The rfkill class is provided for kernel drivers to register their radio -transmitter with the kernel, provide methods for turning it on and off and, -optionally, letting the system know about hardware-disabled states that may -be implemented on the device. This code is enabled with the CONFIG_RFKILL -Kconfig option, which drivers can "select". - -The rfkill class code also notifies userspace of state changes, this is -achieved via uevents. It also provides some sysfs files for userspace to -check the status of radio transmitters. See the "Userspace support" section -below. +The rfkill subsystem is composed of three main components: + * the rfkill core, + * the deprecated rfkill-input module (an input layer handler, being + replaced by userspace policy code) and + * the rfkill drivers. +The rfkill core provides API for kernel drivers to register their radio +transmitter with the kernel, methods for turning it on and off and, letting +the system know about hardware-disabled states that may be implemented on +the device. -The rfkill-input code implements a basic response to rfkill buttons -- it -implements turning on/off all devices of a certain class (or all). +The rfkill core code also notifies userspace of state changes, and provides +ways for userspace to query the current states. See the "Userspace support" +section below. When the device is hard-blocked (either by a call to rfkill_set_hw_state() -or from query_hw_block) set_block() will be invoked but drivers can well -ignore the method call since they can use the return value of the function -rfkill_set_hw_state() to sync the software state instead of keeping track -of calls to set_block(). - - -The entire functionality is spread over more than one subsystem: - - * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and - SW_RFKILL_ALL -- when the user presses a button. Drivers for radio - transmitters generally do not register to the input layer, unless the - device really provides an input device (i.e. a button that has no - effect other than generating a button press event) - - * The rfkill-input code hooks up to these events and switches the soft-block - of the various radio transmitters, depending on the button type. - - * The rfkill drivers turn off/on their transmitters as requested. - - * The rfkill class will generate userspace notifications (uevents) to tell - userspace what the current state is. +or from query_hw_block) set_block() will be invoked for additional software +block, but drivers can ignore the method call since they can use the return +value of the function rfkill_set_hw_state() to sync the software state +instead of keeping track of calls to set_block(). In fact, drivers should +use the return value of rfkill_set_hw_state() unless the hardware actually +keeps track of soft and hard block separately. +3. Kernel API -3. Kernel driver guidelines - -Drivers for radio transmitters normally implement only the rfkill class. -These drivers may not unblock the transmitter based on own decisions, they -should act on information provided by the rfkill class only. +Drivers for radio transmitters normally implement an rfkill driver. Platform drivers might implement input devices if the rfkill button is just that, a button. If that button influences the hardware then you need to -implement an rfkill class instead. This also applies if the platform provides +implement an rfkill driver instead. This also applies if the platform provides a way to turn on/off the transmitter(s). -During suspend/hibernation, transmitters should only be left enabled when -wake-on wlan or similar functionality requires it and the device wasn't -blocked before suspend/hibernate. Note that it may be necessary to update -the rfkill subsystem's idea of what the current state is at resume time if -the state may have changed over suspend. - +For some platforms, it is possible that the hardware state changes during +suspend/hibernation, in which case it will be necessary to update the rfkill +core with the current state is at resume time. +To create an rfkill driver, driver's Kconfig needs to have -4. Kernel API + depends on RFKILL || !RFKILL -To build a driver with rfkill subsystem support, the driver should depend on -(or select) the Kconfig symbol RFKILL. - -The hardware the driver talks to may be write-only (where the current state -of the hardware is unknown), or read-write (where the hardware can be queried -about its current state). +to ensure the driver cannot be built-in when rfkill is modular. The !RFKILL +case allows the driver to be built when rfkill is not configured, which which +case all rfkill API can still be used but will be provided by static inlines +which compile to almost nothing. Calling rfkill_set_hw_state() when a state change happens is required from rfkill drivers that control devices that can be hard-blocked unless they also @@ -105,10 +84,33 @@ device). Don't do this unless you cannot get the event in any other way. 5. Userspace support -The following sysfs entries exist for every rfkill device: +The recommended userspace interface to use is /dev/rfkill, which is a misc +character device that allows userspace to obtain and set the state of rfkill +devices and sets of devices. It also notifies userspace about device addition +and removal. The API is a simple read/write API that is defined in +linux/rfkill.h, with one ioctl that allows turning off the deprecated input +handler in the kernel for the transition period. + +Except for the one ioctl, communication with the kernel is done via read() +and write() of instances of 'struct rfkill_event'. In this structure, the +soft and hard block are properly separated (unlike sysfs, see below) and +userspace is able to get a consistent snapshot of all rfkill devices in the +system. Also, it is possible to switch all rfkill drivers (or all drivers of +a specified type) into a state which also updates the default state for +hotplugged devices. + +After an application opens /dev/rfkill, it can read the current state of +all devices, and afterwards can poll the descriptor for hotplug or state +change events. + +Applications must ignore operations (the "op" field) they do not handle, +this allows the API to be extended in the future. + +Additionally, each rfkill device is registered in sysfs and there has the +following attributes: name: Name assigned by driver to this key (interface or driver name). - type: Name of the key type ("wlan", "bluetooth", etc). + type: Driver type string ("wlan", "bluetooth", etc). state: Current state of the transmitter 0: RFKILL_STATE_SOFT_BLOCKED transmitter is turned off by software @@ -117,7 +119,12 @@ The following sysfs entries exist for every rfkill device: 2: RFKILL_STATE_HARD_BLOCKED transmitter is forced off by something outside of the driver's control. - claim: 0: Kernel handles events (currently always reads that value) + This file is deprecated because it can only properly show + three of the four possible states, soft-and-hard-blocked is + missing. + claim: 0: Kernel handles events + This file is deprecated because there no longer is a way to + claim just control over a single rfkill instance. rfkill devices also issue uevents (with an action of "change"), with the following environment variables set: @@ -128,9 +135,3 @@ RFKILL_TYPE The contents of these variables corresponds to the "name", "state" and "type" sysfs files explained above. - -An alternative userspace interface exists as a misc device /dev/rfkill, -which allows userspace to obtain and set the state of rfkill devices and -sets of devices. It also notifies userspace about device addition and -removal. The API is a simple read/write API that is defined in -linux/rfkill.h. -- cgit v0.10.2 From 624f0de44afa9fbb4ec8396f88d8d54b96a57a7e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 15 Jun 2009 16:26:47 +0200 Subject: dell-laptop: fix rfkill conversion A polarity error snuck into the rfkill rewrite's dell-laptop conversion, fix it. Signed-off-by: Johannes Berg Tested-by: Oliver Hartkopp Signed-off-by: John W. Linville diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 2faf0e1..74909c4 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -177,7 +177,7 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, static int dell_rfkill_set(void *data, bool blocked) { struct calling_interface_buffer buffer; - int disable = blocked ? 0 : 1; + int disable = blocked ? 1 : 0; unsigned long radio = (unsigned long)data; memset(&buffer, 0, sizeof(struct calling_interface_buffer)); -- cgit v0.10.2 From 3989279ccc838c7357c75af6090f56f5cb555158 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 15 Jun 2009 17:49:09 +0200 Subject: ath9k: restore power mode on error path The ath9k_ps_{wakeup,restore} calls must be in balance. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 91c0048..9f49a32 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -296,7 +296,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, "reset status %d\n", channel->center_freq, r); spin_unlock_bh(&sc->sc_resetlock); - return r; + goto ps_restore; } spin_unlock_bh(&sc->sc_resetlock); @@ -305,14 +305,17 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, if (ath_startrecv(sc) != 0) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to restart recv logic\n"); - return -EIO; + r = -EIO; + goto ps_restore; } ath_cache_conf_rate(sc, &hw->conf); ath_update_txpow(sc); ath9k_hw_set_interrupts(ah, sc->imask); + + ps_restore: ath9k_ps_restore(sc); - return 0; + return r; } /* -- cgit v0.10.2 From 7fe96a16486fb617a26b39a4895c7522c50fc555 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 15 Jun 2009 17:49:10 +0200 Subject: ath9k: prevent sleeping while we are waiting for CAB We have to remain awake if the SC_OP_WAIT_FOR_CAB flag is set. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a6e65ff..5efc934 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -668,6 +668,7 @@ static inline void ath9k_ps_restore(struct ath_softc *sc) if (atomic_dec_and_test(&sc->ps_usecount)) if ((sc->hw->conf.flags & IEEE80211_CONF_PS) && !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_PSPOLL_DATA | SC_OP_WAIT_FOR_TX_ACK))) ath9k_hw_setpower(sc->sc_ah, -- cgit v0.10.2 From f0e9a8606ce60880249fd570fbebf4472c3d37c0 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 15 Jun 2009 17:49:11 +0200 Subject: ath9k: process rx packet if we are waiting for CAB If we are in PS mode, we have to process the received frame if the SC_OP_WAIT_FOR_CAB bit is set. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 5014a19..f99f3a7 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -817,6 +817,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) } if (unlikely(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_PSPOLL_DATA))) ath_rx_ps(sc, skb); -- cgit v0.10.2 From 1fa6f4af9f55bc1b753af04276984429d6b5a613 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 15 Jun 2009 18:13:58 +0200 Subject: mac80211: fix wext bssid/ssid setting When changing to a new BSSID or SSID, the code in ieee80211_set_disassoc() needs to have the old data still valid to be able to disconnect and clean up properly. Currently, however, the old data is thrown away before ieee80211_set_disassoc() is ever called, so fix that by calling the function _before_ the old data is overwritten. This is (one of) the issue(s) causing mac80211 to hold cfg80211's BSS structs forever, and them thus being returned in scan results after they're long gone. http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=2015 Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 84e59b9..aca22b0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1102,14 +1102,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; u32 changed = 0, config_changed = 0; - rcu_read_lock(); - - sta = sta_info_get(local, ifmgd->bssid); - if (!sta) { - rcu_read_unlock(); - return; - } - if (deauth) { ifmgd->direct_probe_tries = 0; ifmgd->auth_tries = 0; @@ -1120,7 +1112,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); - ieee80211_sta_tear_down_BA_sessions(sta); + rcu_read_lock(); + sta = sta_info_get(local, ifmgd->bssid); + if (sta) + ieee80211_sta_tear_down_BA_sessions(sta); + rcu_read_unlock(); bss = ieee80211_rx_bss_get(local, ifmgd->bssid, conf->channel->center_freq, @@ -1156,8 +1152,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->ssid, ifmgd->ssid_len); } - rcu_read_unlock(); - ieee80211_set_wmm_default(sdata); ieee80211_recalc_idle(local); @@ -2487,6 +2481,10 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size ifmgd = &sdata->u.mgd; if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) { + if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) + ieee80211_set_disassoc(sdata, true, true, + WLAN_REASON_DEAUTH_LEAVING); + /* * Do not use reassociation if SSID is changed (different ESS). */ @@ -2511,6 +2509,11 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + if (compare_ether_addr(bssid, ifmgd->bssid) != 0 && + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) + ieee80211_set_disassoc(sdata, true, true, + WLAN_REASON_DEAUTH_LEAVING); + if (is_valid_ether_addr(bssid)) { memcpy(ifmgd->bssid, bssid, ETH_ALEN); ifmgd->flags |= IEEE80211_STA_BSSID_SET; -- cgit v0.10.2 From 6cc90a5a6061428358d0f726a53fb44af5254111 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 11 Jun 2009 07:03:47 +0000 Subject: sky2: don't look for VPD size The code to compute VPD size didn't handle some systems that use chip without VPD. Also some of the newer chips use some additional registers to store the actual size, and wasn't worth putting the additional complexity in, so just remove the code. No big loss since the code to set the VPD size was only a convenience so that utilities would not read the extra space past the end of the available VPD. Move the first PCI config read earlier to detect bad hardware where it returns all ones and refuse loading driver before furthur damage. Signed-off-by: Stephen Hemminger Tested-by: Andy Whitcroft Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6b5946f..c6ceba9 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -4364,6 +4364,22 @@ static int __devinit sky2_probe(struct pci_dev *pdev, goto err_out; } + /* Get configuration information + * Note: only regular PCI config access once to test for HW issues + * other PCI access through shared memory for speed and to + * avoid MMCONFIG problems. + */ + err = pci_read_config_dword(pdev, PCI_DEV_REG2, ®); + if (err) { + dev_err(&pdev->dev, "PCI read config failed\n"); + goto err_out; + } + + if (~reg == 0) { + dev_err(&pdev->dev, "PCI configuration read error\n"); + goto err_out; + } + err = pci_request_regions(pdev, DRV_NAME); if (err) { dev_err(&pdev->dev, "cannot obtain PCI resources\n"); @@ -4389,21 +4405,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } } - /* Get configuration information - * Note: only regular PCI config access once to test for HW issues - * other PCI access through shared memory for speed and to - * avoid MMCONFIG problems. - */ - err = pci_read_config_dword(pdev, PCI_DEV_REG2, ®); - if (err) { - dev_err(&pdev->dev, "PCI read config failed\n"); - goto err_out_free_regions; - } - - /* size of available VPD, only impact sysfs */ - err = pci_vpd_truncate(pdev, 1ul << (((reg & PCI_VPD_ROM_SZ) >> 14) + 8)); - if (err) - dev_warn(&pdev->dev, "Can't set VPD size\n"); #ifdef __BIG_ENDIAN /* The sk98lin vendor driver uses hardware byte swapping but -- cgit v0.10.2 From 14ebaf81e13ce66bff275380b246796fd16cbfa1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 16 Jun 2009 05:40:30 -0700 Subject: x25: Fix sleep from timer on socket destroy. If socket destuction gets delayed to a timer, we try to lock_sock() from that timer which won't work. Use bh_lock_sock() in that case. Signed-off-by: David S. Miller Tested-by: Ingo Molnar diff --git a/include/net/x25.h b/include/net/x25.h index fc3f03d..2cda040 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -187,7 +187,7 @@ extern int x25_addr_ntoa(unsigned char *, struct x25_address *, extern int x25_addr_aton(unsigned char *, struct x25_address *, struct x25_address *); extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *); -extern void x25_destroy_socket(struct sock *); +extern void x25_destroy_socket_from_timer(struct sock *); extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int); extern void x25_kill_by_neigh(struct x25_neigh *); diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index ed80af8..c51f309 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -332,14 +332,14 @@ static unsigned int x25_new_lci(struct x25_neigh *nb) /* * Deferred destroy. */ -void x25_destroy_socket(struct sock *); +static void __x25_destroy_socket(struct sock *); /* * handler for deferred kills. */ static void x25_destroy_timer(unsigned long data) { - x25_destroy_socket((struct sock *)data); + x25_destroy_socket_from_timer((struct sock *)data); } /* @@ -349,12 +349,10 @@ static void x25_destroy_timer(unsigned long data) * will touch it and we are (fairly 8-) ) safe. * Not static as it's used by the timer */ -void x25_destroy_socket(struct sock *sk) +static void __x25_destroy_socket(struct sock *sk) { struct sk_buff *skb; - sock_hold(sk); - lock_sock(sk); x25_stop_heartbeat(sk); x25_stop_timer(sk); @@ -385,7 +383,22 @@ void x25_destroy_socket(struct sock *sk) /* drop last reference so sock_put will free */ __sock_put(sk); } +} +void x25_destroy_socket_from_timer(struct sock *sk) +{ + sock_hold(sk); + bh_lock_sock(sk); + __x25_destroy_socket(sk); + bh_unlock_sock(sk); + sock_put(sk); +} + +static void x25_destroy_socket(struct sock *sk) +{ + sock_hold(sk); + lock_sock(sk); + __x25_destroy_socket(sk); release_sock(sk); sock_put(sk); } diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index d3e3e54..5c5db1a 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.c @@ -113,7 +113,7 @@ static void x25_heartbeat_expiry(unsigned long param) (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { bh_unlock_sock(sk); - x25_destroy_socket(sk); + x25_destroy_socket_from_timer(sk); return; } break; -- cgit v0.10.2 From 05e9e61a2e742d33ed24392d989fff912a68d04f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 17 Jun 2009 01:36:23 -0700 Subject: sonic: Fix build after ndo_start_xmit() changes. Noticed by Stephen Rothwell. Signed-off-by: David S. Miller diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c index e4255d8..753a1fb 100644 --- a/drivers/net/sonic.c +++ b/drivers/net/sonic.c @@ -223,7 +223,7 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) if (!laddr) { printk(KERN_ERR "%s: failed to map tx DMA buffer.\n", dev->name); dev_kfree_skb(skb); - return NETDEV_TX_BUSY + return NETDEV_TX_BUSY; } sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0); /* clear status */ -- cgit v0.10.2 From a42fc8f6943127787ad2a416436cf211d5531229 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 16 Jun 2009 16:56:38 +0000 Subject: skbuff.h: fix skb_dst kernel-doc Fix kernel-doc warnings (missing + extra entries) in skbuff.h. Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index fa51293..3d28936 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -264,7 +264,7 @@ typedef unsigned char *sk_buff_data_t; * @transport_header: Transport layer header * @network_header: Network layer header * @mac_header: Link layer header - * @dst: destination entry + * @_skb_dst: destination entry * @sp: the security path, used for xfrm * @cb: Control buffer. Free for use by every layer. Put private vars here * @len: Length of actual data -- cgit v0.10.2 From 5dbc901172fb952409940cd7ca55d8e6e5a7cc2c Mon Sep 17 00:00:00 2001 From: Sivakumar Subramani Date: Tue, 16 Jun 2009 18:48:55 +0000 Subject: vxge: Enable SRIOV support in the driver. - Enabled SRIOV support in the driver. - Call __vxge_hw_verify_pci_e_info() for the PF only. This function verifies the negotiated link width and current link speed in the Link Status Register (offset 12h) which are reserved fields for VFs as per the SRIOV specification, section 3.5.8. - Implemented David Miller's comment to remove the #ifdef CONFIG_PCI_IOV as these intefaces have NOP versions declared when the defintion is not set. Signed-off-by: Sivakumar Subramani Signed-off-by: Rastapur Santosh Signed-off-by: Ramkrishna Vepa Signed-off-by: David S. Miller diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/vxge/vxge-config.c index 26cde57..58d2551 100644 --- a/drivers/net/vxge/vxge-config.c +++ b/drivers/net/vxge/vxge-config.c @@ -454,7 +454,7 @@ __vxge_hw_verify_pci_e_info(struct __vxge_hw_device *hldev) return VXGE_HW_OK; } -static enum vxge_hw_status +enum vxge_hw_status __vxge_hw_device_is_privilaged(struct __vxge_hw_device *hldev) { if ((hldev->host_type == VXGE_HW_NO_MR_NO_SR_NORMAL_FUNCTION || @@ -676,10 +676,12 @@ enum vxge_hw_status __vxge_hw_device_initialize(struct __vxge_hw_device *hldev) { enum vxge_hw_status status = VXGE_HW_OK; - /* Validate the pci-e link width and speed */ - status = __vxge_hw_verify_pci_e_info(hldev); - if (status != VXGE_HW_OK) - goto exit; + if (VXGE_HW_OK == __vxge_hw_device_is_privilaged(hldev)) { + /* Validate the pci-e link width and speed */ + status = __vxge_hw_verify_pci_e_info(hldev); + if (status != VXGE_HW_OK) + goto exit; + } vxge_hw_wrr_rebalance(hldev); exit: diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c index 6c838b3..6034497 100644 --- a/drivers/net/vxge/vxge-main.c +++ b/drivers/net/vxge/vxge-main.c @@ -4203,6 +4203,16 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) max_vpath_supported++; } + /* Enable SRIOV mode, if firmware has SRIOV support and if it is a PF */ + if ((VXGE_HW_FUNCTION_MODE_SRIOV == + ll_config.device_hw_info.function_mode) && + (max_config_dev > 1) && (pdev->is_physfn)) { + ret = pci_enable_sriov(pdev, max_config_dev - 1); + if (ret) + vxge_debug_ll_config(VXGE_ERR, + "Failed to enable SRIOV: %d \n", ret); + } + /* * Configure vpaths and get driver configured number of vpaths * which is less than or equal to the maximum vpaths per function. @@ -4366,6 +4376,7 @@ _exit6: vxge_device_unregister(hldev); _exit5: + pci_disable_sriov(pdev); vxge_hw_device_terminate(hldev); _exit4: iounmap(attr.bar1); @@ -4429,6 +4440,8 @@ vxge_remove(struct pci_dev *pdev) iounmap(vdev->bar0); iounmap(vdev->bar1); + pci_disable_sriov(pdev); + /* we are safe to free it now */ free_netdev(dev); diff --git a/drivers/net/vxge/vxge-version.h b/drivers/net/vxge/vxge-version.h index 7da02c5..82786ff 100644 --- a/drivers/net/vxge/vxge-version.h +++ b/drivers/net/vxge/vxge-version.h @@ -17,7 +17,7 @@ #define VXGE_VERSION_MAJOR "2" #define VXGE_VERSION_MINOR "0" -#define VXGE_VERSION_FIX "1" -#define VXGE_VERSION_BUILD "17129" +#define VXGE_VERSION_FIX "4" +#define VXGE_VERSION_BUILD "17795" #define VXGE_VERSION_FOR "k" #endif -- cgit v0.10.2 From d77eeb702cba0fa6efaf5689fce8939aa617f410 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 15 Jun 2009 23:33:24 +0000 Subject: net: fix network drivers ndo_start_xmit() return values Fix up remaining drivers returning a magic or an errno value from their ndo_start_xmit() functions that were missed in the first pass: - isdn_net: missed conversion - bpqether: missed conversion: skb is freed, so return NETDEV_TX_OK - hp100: intention appears to be to resubmit skb once resources are available, but due to no queue handling it is dropped for now. - lapbether: skb is freed, so return NETDEV_TX_OK Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 34d54e7..de4aad0 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1300,7 +1300,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) netif_stop_queue(ndev); } } - return 1; + return NETDEV_TX_BUSY; } /* diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 5105548..abcd19a 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -260,7 +260,7 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev) */ if (!netif_running(dev)) { kfree_skb(skb); - return -ENODEV; + return NETDEV_TX_OK; } skb_pull(skb, 1); diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 8feda9f..1d3429a 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -1495,13 +1495,8 @@ static int hp100_start_xmit_bm(struct sk_buff *skb, struct net_device *dev) hp100_outw(0x4210, TRACE); printk("hp100: %s: start_xmit_bm\n", dev->name); #endif - - if (skb == NULL) { - return 0; - } - if (skb->len <= 0) - return 0; + goto drop; if (lp->chip == HP100_CHIPID_SHASTA && skb_padto(skb, ETH_ZLEN)) return 0; @@ -1514,10 +1509,10 @@ static int hp100_start_xmit_bm(struct sk_buff *skb, struct net_device *dev) #endif /* not waited long enough since last tx? */ if (time_before(jiffies, dev->trans_start + HZ)) - return -EAGAIN; + goto drop; if (hp100_check_lan(dev)) - return -EIO; + goto drop; if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0) { /* we have a 100Mb/s adapter but it isn't connected to hub */ @@ -1551,7 +1546,7 @@ static int hp100_start_xmit_bm(struct sk_buff *skb, struct net_device *dev) } dev->trans_start = jiffies; - return -EAGAIN; + goto drop; } /* @@ -1591,6 +1586,10 @@ static int hp100_start_xmit_bm(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; return 0; + +drop: + dev_kfree_skb(skb); + return NETDEV_TX_OK; } @@ -1648,16 +1647,11 @@ static int hp100_start_xmit(struct sk_buff *skb, struct net_device *dev) hp100_outw(0x4212, TRACE); printk("hp100: %s: start_xmit\n", dev->name); #endif - - if (skb == NULL) { - return 0; - } - if (skb->len <= 0) - return 0; + goto drop; if (hp100_check_lan(dev)) - return -EIO; + goto drop; /* If there is not enough free memory on the card... */ i = hp100_inl(TX_MEM_FREE) & 0x7fffffff; @@ -1671,7 +1665,7 @@ static int hp100_start_xmit(struct sk_buff *skb, struct net_device *dev) printk("hp100: %s: trans_start timing problem\n", dev->name); #endif - return -EAGAIN; + goto drop; } if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0) { /* we have a 100Mb/s adapter but it isn't connected to hub */ @@ -1705,7 +1699,7 @@ static int hp100_start_xmit(struct sk_buff *skb, struct net_device *dev) } } dev->trans_start = jiffies; - return -EAGAIN; + goto drop; } for (i = 0; i < 6000 && (hp100_inb(OPTION_MSW) & HP100_TX_CMD); i++) { @@ -1759,6 +1753,11 @@ static int hp100_start_xmit(struct sk_buff *skb, struct net_device *dev) #endif return 0; + +drop: + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index 2dd78d2..aff4f6bd 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -149,46 +149,40 @@ static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb) */ static int lapbeth_xmit(struct sk_buff *skb, struct net_device *dev) { - int err = -ENODEV; + int err; /* * Just to be *really* sure not to send anything if the interface * is down, the ethernet device may have gone. */ - if (!netif_running(dev)) { + if (!netif_running(dev)) goto drop; - } switch (skb->data[0]) { case 0x00: - err = 0; break; case 0x01: if ((err = lapb_connect_request(dev)) != LAPB_OK) printk(KERN_ERR "lapbeth: lapb_connect_request " "error: %d\n", err); - goto drop_ok; + goto drop; case 0x02: if ((err = lapb_disconnect_request(dev)) != LAPB_OK) printk(KERN_ERR "lapbeth: lapb_disconnect_request " "err: %d\n", err); /* Fall thru */ default: - goto drop_ok; + goto drop; } skb_pull(skb, 1); if ((err = lapb_data_request(dev, skb)) != LAPB_OK) { printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err); - err = -ENOMEM; goto drop; } - err = 0; out: - return err; -drop_ok: - err = 0; + return NETDEV_TX_OK; drop: kfree_skb(skb); goto out; -- cgit v0.10.2 From 1d4ac5d5ef9dd965ae211ebe8acbf83dc4d9571b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 16 Jun 2009 16:56:33 +0000 Subject: phy_device: fix parameter name in kernel-doc Fix kernel-doc parameter name in phy_device.c. Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a2ece89..eba937c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -244,7 +244,7 @@ EXPORT_SYMBOL(get_phy_device); /** * phy_device_register - Register the phy device on the MDIO bus - * @phy_device: phy_device structure to be added to the MDIO bus + * @phydev: phy_device structure to be added to the MDIO bus */ int phy_device_register(struct phy_device *phydev) { -- cgit v0.10.2 From c564039fd83ea16a86a96d52632794b24849e507 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 16 Jun 2009 10:12:03 +0000 Subject: net: sk_wmem_alloc has initial value of one, not zero commit 2b85a34e911bf483c27cfdd124aeb1605145dc80 (net: No more expensive sock_hold()/sock_put() on each tx) changed initial sk_wmem_alloc value. Some protocols check sk_wmem_alloc value to determine if a timer must delay socket deallocation. We must take care of the sk_wmem_alloc value being one instead of zero when no write allocations are pending. Reported by Ingo Molnar, and full diagnostic from David Miller. This patch introduces three helpers to get read/write allocations and a followup patch will use these helpers to report correct write allocations to user. Reported-by: Ingo Molnar Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index 010e14a..570c7a1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1206,6 +1206,39 @@ static inline int skb_copy_to_page(struct sock *sk, char __user *from, return 0; } +/** + * sk_wmem_alloc_get - returns write allocations + * @sk: socket + * + * Returns sk_wmem_alloc minus initial offset of one + */ +static inline int sk_wmem_alloc_get(const struct sock *sk) +{ + return atomic_read(&sk->sk_wmem_alloc) - 1; +} + +/** + * sk_rmem_alloc_get - returns read allocations + * @sk: socket + * + * Returns sk_rmem_alloc + */ +static inline int sk_rmem_alloc_get(const struct sock *sk) +{ + return atomic_read(&sk->sk_rmem_alloc); +} + +/** + * sk_has_allocations - check if allocations are outstanding + * @sk: socket + * + * Returns true if socket has write or read allocations + */ +static inline int sk_has_allocations(const struct sock *sk) +{ + return sk_wmem_alloc_get(sk) || sk_rmem_alloc_get(sk); +} + /* * Queue a received datagram if it will fit. Stream and sequenced * protocols can't normally use this as they need to fit buffers in diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index b603cba..f7a53b2 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -162,8 +162,7 @@ static void atalk_destroy_timer(unsigned long data) { struct sock *sk = (struct sock *)data; - if (atomic_read(&sk->sk_wmem_alloc) || - atomic_read(&sk->sk_rmem_alloc)) { + if (sk_has_allocations(sk)) { sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; add_timer(&sk->sk_timer); } else @@ -175,8 +174,7 @@ static inline void atalk_destroy_socket(struct sock *sk) atalk_remove_socket(sk); skb_queue_purge(&sk->sk_receive_queue); - if (atomic_read(&sk->sk_wmem_alloc) || - atomic_read(&sk->sk_rmem_alloc)) { + if (sk_has_allocations(sk)) { setup_timer(&sk->sk_timer, atalk_destroy_timer, (unsigned long)sk); sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index fd9d06f..61b35b9 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -330,8 +330,7 @@ void ax25_destroy_socket(ax25_cb *ax25) } if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->sk_wmem_alloc) || - atomic_read(&ax25->sk->sk_rmem_alloc)) { + if (sk_has_allocations(ax25->sk)) { /* Defer: outstanding buffers */ setup_timer(&ax25->dtimer, ax25_destroy_timer, (unsigned long)ax25); diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 8121bf0..2e1f836 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -540,8 +540,7 @@ static void econet_destroy_timer(unsigned long data) { struct sock *sk=(struct sock *)data; - if (!atomic_read(&sk->sk_wmem_alloc) && - !atomic_read(&sk->sk_rmem_alloc)) { + if (!sk_has_allocations(sk)) { sk_free(sk); return; } @@ -579,8 +578,7 @@ static int econet_release(struct socket *sock) skb_queue_purge(&sk->sk_receive_queue); - if (atomic_read(&sk->sk_rmem_alloc) || - atomic_read(&sk->sk_wmem_alloc)) { + if (sk_has_allocations(sk)) { sk->sk_timer.data = (unsigned long)sk; sk->sk_timer.expires = jiffies + HZ; sk->sk_timer.function = econet_destroy_timer; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 3be0e01..cd91190 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -286,8 +286,7 @@ void nr_destroy_socket(struct sock *sk) kfree_skb(skb); } - if (atomic_read(&sk->sk_wmem_alloc) || - atomic_read(&sk->sk_rmem_alloc)) { + if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ sk->sk_timer.function = nr_destroy_timer; sk->sk_timer.expires = jiffies + 2 * HZ; diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 877a7f6..4dd9a7d 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -356,8 +356,7 @@ void rose_destroy_socket(struct sock *sk) kfree_skb(skb); } - if (atomic_read(&sk->sk_wmem_alloc) || - atomic_read(&sk->sk_rmem_alloc)) { + if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ setup_timer(&sk->sk_timer, rose_destroy_timer, (unsigned long)sk); diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index c51f309..8cd2390 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -372,8 +372,7 @@ static void __x25_destroy_socket(struct sock *sk) kfree_skb(skb); } - if (atomic_read(&sk->sk_wmem_alloc) || - atomic_read(&sk->sk_rmem_alloc)) { + if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; -- cgit v0.10.2 From 19633e129c65e5bb62b1af545c5479afcdb01fc4 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 05:23:27 +0000 Subject: skbuff: skb_mac_header_was_set is always true on >32 bit Looking at the crash in log_martians(), one suspect is that the check for mac header being set is not correct. The value of mac_header defaults to 0 on allocation, therefore skb_mac_header_was_set will always be true on platforms using NET_SKBUFF_USES_OFFSET. Signed-off-by: Stephen Hemminger Acked-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1a94a30..436695d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -201,6 +201,10 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, skb->data = data; skb_reset_tail_pointer(skb); skb->end = skb->tail + size; +#ifdef NET_SKBUFF_DATA_USES_OFFSET + skb->mac_header = ~0U; +#endif + /* make sure we initialize shinfo sequentially */ shinfo = skb_shinfo(skb); atomic_set(&shinfo->dataref, 1); -- cgit v0.10.2 From 603a8bbe62e54108055fca46ecdd611c10c6cd0a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 12:17:34 +0000 Subject: skbuff: don't corrupt mac_header on skb expansion The skb mac_header field is sometimes NULL (or ~0u) as a sentinel value. The places where skb is expanded add an offset which would change this flag into an invalid pointer (or offset). Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 436695d..a4c01f5 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -661,7 +661,8 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) /* {transport,network,mac}_header are relative to skb->head */ new->transport_header += offset; new->network_header += offset; - new->mac_header += offset; + if (skb_mac_header_was_set(new)) + new->mac_header += offset; #endif skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; @@ -843,7 +844,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb->tail += off; skb->transport_header += off; skb->network_header += off; - skb->mac_header += off; + if (skb_mac_header_was_set(skb)) + skb->mac_header += off; skb->csum_start += nhead; skb->cloned = 0; skb->hdr_len = 0; @@ -935,7 +937,8 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, #ifdef NET_SKBUFF_DATA_USES_OFFSET n->transport_header += off; n->network_header += off; - n->mac_header += off; + if (skb_mac_header_was_set(skb)) + n->mac_header += off; #endif return n; -- cgit v0.10.2 From f3a32500ba8f3ec9ee0c12836fcfd315f1256db4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 17 Jun 2009 13:16:04 +0000 Subject: Revert "net/ucc_geth: Add SGMII support for UEC GETH driver" This reverts commit 047584ce94108012288554a5f84585d792cc7f8f. This patch meshes badly with "net: Rework ucc_geth driver to use of_mdio infrastructure" (0b9da337dca972e7a4144e298ec3adb8f244d4a4). Since most of the patch needs to be reworked, it is clearer to revert the patch and then apply the corrected version Signed-off-by: Grant Likely Signed-off-by: David S. Miller diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h index 4459d20..2701753 100644 --- a/arch/powerpc/include/asm/qe.h +++ b/arch/powerpc/include/asm/qe.h @@ -668,8 +668,6 @@ struct ucc_slow_pram { #define UCC_GETH_UPSMR_RMM 0x00001000 #define UCC_GETH_UPSMR_CAM 0x00000400 #define UCC_GETH_UPSMR_BRO 0x00000200 -#define UCC_GETH_UPSMR_SMM 0x00000080 -#define UCC_GETH_UPSMR_SGMM 0x00000020 /* UCC Transmit On Demand Register (UTODR) */ #define UCC_SLOW_TOD 0x8000 diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index fd6140b..0cf22c4 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Freescale Semicondutor, Inc. All rights reserved. + * Copyright (C) 2006-2007 Freescale Semicondutor, Inc. All rights reserved. * * Author: Shlomi Gridish * Li Yang @@ -65,8 +65,6 @@ static DEFINE_SPINLOCK(ugeth_lock); -static void uec_configure_serdes(struct net_device *dev); - static struct { u32 msg_enable; } debug = { -1 }; @@ -1412,9 +1410,6 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UCC_GETH_UPSMR_TBIM; } - if ((ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII)) - upsmr |= UCC_GETH_UPSMR_SGMM; - out_be32(&uf_regs->upsmr, upsmr); /* Disable autonegotiation in tbi mode, because by default it @@ -1559,9 +1554,6 @@ static int init_phy(struct net_device *dev) return -ENODEV; } - if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII) - uec_configure_serdes(dev); - phydev->supported &= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | @@ -1577,41 +1569,7 @@ static int init_phy(struct net_device *dev) return 0; } -/* Initialize TBI PHY interface for communicating with the - * SERDES lynx PHY on the chip. We communicate with this PHY - * through the MDIO bus on each controller, treating it as a - * "normal" PHY at the address found in the UTBIPA register. We assume - * that the UTBIPA register is valid. Either the MDIO bus code will set - * it to a value that doesn't conflict with other PHYs on the bus, or the - * value doesn't matter, as there are no other PHYs on the bus. - */ -static void uec_configure_serdes(struct net_device *dev) -{ - struct ucc_geth_private *ugeth = netdev_priv(dev); - - if (!ugeth->tbiphy) { - printk(KERN_WARNING "SGMII mode requires that the device " - "tree specify a tbi-handle\n"); - return; - } - - /* - * If the link is already up, we must already be ok, and don't need to - * configure and reset the TBI<->SerDes link. Maybe U-Boot configured - * everything for us? Resetting it takes the link down and requires - * several seconds for it to come back. - */ - if (phy_read(ugeth->tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) - return; - - /* Single clk mode, mii mode off(for serdes communication) */ - phy_write(ugeth->tbiphy, ENET_TBI_MII_ANA, TBIANA_SETTINGS); - phy_write(ugeth->tbiphy, ENET_TBI_MII_TBICON, TBICON_CLK_SELECT); - - phy_write(ugeth->tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS); - -} static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth) { @@ -3565,8 +3523,6 @@ static phy_interface_t to_phy_interface(const char *phy_connection_type) return PHY_INTERFACE_MODE_RGMII_RXID; if (strcasecmp(phy_connection_type, "rtbi") == 0) return PHY_INTERFACE_MODE_RTBI; - if (strcasecmp(phy_connection_type, "sgmii") == 0) - return PHY_INTERFACE_MODE_SGMII; return PHY_INTERFACE_MODE_MII; } @@ -3611,7 +3567,6 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI, - PHY_INTERFACE_MODE_SGMII, }; ugeth_vdbg("%s: IN", __func__); @@ -3727,7 +3682,6 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_TBI: case PHY_INTERFACE_MODE_RTBI: - case PHY_INTERFACE_MODE_SGMII: max_speed = SPEED_1000; break; default: @@ -3802,37 +3756,6 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma ugeth->ndev = dev; ugeth->node = np; - /* Find the TBI PHY. If it's not there, we don't support SGMII */ - ph = of_get_property(np, "tbi-handle", NULL); - if (ph) { - struct device_node *tbi = of_find_node_by_phandle(*ph); - struct of_device *ofdev; - struct mii_bus *bus; - const unsigned int *id; - - if (!tbi) - return 0; - - mdio = of_get_parent(tbi); - if (!mdio) - return 0; - - ofdev = of_find_device_by_node(mdio); - - of_node_put(mdio); - - id = of_get_property(tbi, "reg", NULL); - if (!id) - return 0; - of_node_put(tbi); - - bus = dev_get_drvdata(&ofdev->dev); - if (!bus) - return 0; - - ugeth->tbiphy = bus->phy_map[*id]; - } - return 0; } diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h index deb962b..dca628a 100644 --- a/drivers/net/ucc_geth.h +++ b/drivers/net/ucc_geth.h @@ -1,5 +1,5 @@ /* - * Copyright (C) Freescale Semicondutor, Inc. 2006-2009. All rights reserved. + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. * * Author: Shlomi Gridish * @@ -193,31 +193,6 @@ struct ucc_geth { #define ENET_TBI_MII_JD 0x10 /* Jitter diagnostics */ #define ENET_TBI_MII_TBICON 0x11 /* TBI control */ -/* TBI MDIO register bit fields*/ -#define TBISR_LSTATUS 0x0004 -#define TBICON_CLK_SELECT 0x0020 -#define TBIANA_ASYMMETRIC_PAUSE 0x0100 -#define TBIANA_SYMMETRIC_PAUSE 0x0080 -#define TBIANA_HALF_DUPLEX 0x0040 -#define TBIANA_FULL_DUPLEX 0x0020 -#define TBICR_PHY_RESET 0x8000 -#define TBICR_ANEG_ENABLE 0x1000 -#define TBICR_RESTART_ANEG 0x0200 -#define TBICR_FULL_DUPLEX 0x0100 -#define TBICR_SPEED1_SET 0x0040 - -#define TBIANA_SETTINGS ( \ - TBIANA_ASYMMETRIC_PAUSE \ - | TBIANA_SYMMETRIC_PAUSE \ - | TBIANA_FULL_DUPLEX \ - ) -#define TBICR_SETTINGS ( \ - TBICR_PHY_RESET \ - | TBICR_ANEG_ENABLE \ - | TBICR_FULL_DUPLEX \ - | TBICR_SPEED1_SET \ - ) - /* UCC GETH MACCFG1 (MAC Configuration 1 Register) */ #define MACCFG1_FLOW_RX 0x00000020 /* Flow Control Rx */ @@ -1213,7 +1188,6 @@ struct ucc_geth_private { struct ugeth_mii_info *mii_info; struct phy_device *phydev; - struct phy_device *tbiphy; phy_interface_t phy_interface; int max_speed; uint32_t msg_enable; -- cgit v0.10.2 From fb1001f3de1a8576b25d929502f1fe7865ff32dc Mon Sep 17 00:00:00 2001 From: Haiying Wang Date: Wed, 17 Jun 2009 13:16:10 +0000 Subject: net/ucc_geth: Add SGMII support for UCC GETH driver -- derived from reverted commit 047584ce94108012288554a5f84585d792cc7f8f -- reworked by Grant Likely to play nice with commit: "net: Rework ucc_geth driver to use of_mdio infrastructure" (0b9da337dca972e7a4144e298ec3adb8f244d4a4) Signed-off-by: Haiying Wang Signed-off-by: David S. Miller Signed-off-by: Grant Likely Signed-off-by: David S. Miller diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h index 2701753..4459d20 100644 --- a/arch/powerpc/include/asm/qe.h +++ b/arch/powerpc/include/asm/qe.h @@ -668,6 +668,8 @@ struct ucc_slow_pram { #define UCC_GETH_UPSMR_RMM 0x00001000 #define UCC_GETH_UPSMR_CAM 0x00000400 #define UCC_GETH_UPSMR_BRO 0x00000200 +#define UCC_GETH_UPSMR_SMM 0x00000080 +#define UCC_GETH_UPSMR_SGMM 0x00000020 /* UCC Transmit On Demand Register (UTODR) */ #define UCC_SLOW_TOD 0x8000 diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 0cf22c4..255d28d 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Freescale Semicondutor, Inc. All rights reserved. + * Copyright (C) 2006-2009 Freescale Semicondutor, Inc. All rights reserved. * * Author: Shlomi Gridish * Li Yang @@ -1410,6 +1410,9 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UCC_GETH_UPSMR_TBIM; } + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII)) + upsmr |= UCC_GETH_UPSMR_SGMM; + out_be32(&uf_regs->upsmr, upsmr); /* Disable autonegotiation in tbi mode, because by default it @@ -1531,6 +1534,49 @@ static void adjust_link(struct net_device *dev) spin_unlock_irqrestore(&ugeth->lock, flags); } +/* Initialize TBI PHY interface for communicating with the + * SERDES lynx PHY on the chip. We communicate with this PHY + * through the MDIO bus on each controller, treating it as a + * "normal" PHY at the address found in the UTBIPA register. We assume + * that the UTBIPA register is valid. Either the MDIO bus code will set + * it to a value that doesn't conflict with other PHYs on the bus, or the + * value doesn't matter, as there are no other PHYs on the bus. + */ +static void uec_configure_serdes(struct net_device *dev) +{ + struct ucc_geth_private *ugeth = netdev_priv(dev); + struct ucc_geth_info *ug_info = ugeth->ug_info; + struct phy_device *tbiphy; + + if (!ug_info->tbi_node) { + dev_warn(&dev->dev, "SGMII mode requires that the device " + "tree specify a tbi-handle\n"); + return; + } + + tbiphy = of_phy_find_device(ug_info->tbi_node); + if (!tbiphy) { + dev_err(&dev->dev, "error: Could not get TBI device\n"); + return; + } + + /* + * If the link is already up, we must already be ok, and don't need to + * configure and reset the TBI<->SerDes link. Maybe U-Boot configured + * everything for us? Resetting it takes the link down and requires + * several seconds for it to come back. + */ + if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) + return; + + /* Single clk mode, mii mode off(for serdes communication) */ + phy_write(tbiphy, ENET_TBI_MII_ANA, TBIANA_SETTINGS); + + phy_write(tbiphy, ENET_TBI_MII_TBICON, TBICON_CLK_SELECT); + + phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS); +} + /* Configure the PHY for dev. * returns 0 if success. -1 if failure */ @@ -1554,6 +1600,9 @@ static int init_phy(struct net_device *dev) return -ENODEV; } + if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII) + uec_configure_serdes(dev); + phydev->supported &= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | @@ -3523,6 +3572,8 @@ static phy_interface_t to_phy_interface(const char *phy_connection_type) return PHY_INTERFACE_MODE_RGMII_RXID; if (strcasecmp(phy_connection_type, "rtbi") == 0) return PHY_INTERFACE_MODE_RTBI; + if (strcasecmp(phy_connection_type, "sgmii") == 0) + return PHY_INTERFACE_MODE_SGMII; return PHY_INTERFACE_MODE_MII; } @@ -3567,6 +3618,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI, + PHY_INTERFACE_MODE_SGMII, }; ugeth_vdbg("%s: IN", __func__); @@ -3658,6 +3710,9 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma } ug_info->phy_node = phy; + /* Find the TBI PHY node. If it's not there, we don't support SGMII */ + ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0); + /* get the phy interface type, or default to MII */ prop = of_get_property(np, "phy-connection-type", NULL); if (!prop) { @@ -3682,6 +3737,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_TBI: case PHY_INTERFACE_MODE_RTBI: + case PHY_INTERFACE_MODE_SGMII: max_speed = SPEED_1000; break; default: diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h index dca628a..1525bf5 100644 --- a/drivers/net/ucc_geth.h +++ b/drivers/net/ucc_geth.h @@ -1,5 +1,5 @@ /* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. + * Copyright (C) Freescale Semicondutor, Inc. 2006-2009. All rights reserved. * * Author: Shlomi Gridish * @@ -193,6 +193,31 @@ struct ucc_geth { #define ENET_TBI_MII_JD 0x10 /* Jitter diagnostics */ #define ENET_TBI_MII_TBICON 0x11 /* TBI control */ +/* TBI MDIO register bit fields*/ +#define TBISR_LSTATUS 0x0004 +#define TBICON_CLK_SELECT 0x0020 +#define TBIANA_ASYMMETRIC_PAUSE 0x0100 +#define TBIANA_SYMMETRIC_PAUSE 0x0080 +#define TBIANA_HALF_DUPLEX 0x0040 +#define TBIANA_FULL_DUPLEX 0x0020 +#define TBICR_PHY_RESET 0x8000 +#define TBICR_ANEG_ENABLE 0x1000 +#define TBICR_RESTART_ANEG 0x0200 +#define TBICR_FULL_DUPLEX 0x0100 +#define TBICR_SPEED1_SET 0x0040 + +#define TBIANA_SETTINGS ( \ + TBIANA_ASYMMETRIC_PAUSE \ + | TBIANA_SYMMETRIC_PAUSE \ + | TBIANA_FULL_DUPLEX \ + ) +#define TBICR_SETTINGS ( \ + TBICR_PHY_RESET \ + | TBICR_ANEG_ENABLE \ + | TBICR_FULL_DUPLEX \ + | TBICR_SPEED1_SET \ + ) + /* UCC GETH MACCFG1 (MAC Configuration 1 Register) */ #define MACCFG1_FLOW_RX 0x00000020 /* Flow Control Rx */ @@ -1100,6 +1125,7 @@ struct ucc_geth_info { u16 pausePeriod; u16 extensionField; struct device_node *phy_node; + struct device_node *tbi_node; u8 weightfactor[NUM_TX_QUEUES]; u8 interruptcoalescingmaxvalue[NUM_RX_QUEUES]; u8 l2qt[UCC_GETH_VLAN_PRIORITY_MAX]; -- cgit v0.10.2 From c2f3f3a2fe4e35db352cfe1176e384e36bfd91b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20romieu?= Date: Wed, 17 Jun 2009 11:43:11 +0000 Subject: sis190: use an adequate phy list entry as a fallback When sis190 driver is trying to get default phy, if it doesn't find home or lan phy, it falls back to the first phy in the phy list but list_entry() points to a bogus entry. list_first_entry() should be used instead. Signed-off-by: Arnaud Patard Acked-off-by: Francois Romieu Signed-off-by: David S. Miller diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index e224766..1f040e8 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -1281,7 +1281,7 @@ static u16 sis190_default_phy(struct net_device *dev) else if (phy_lan) phy_default = phy_lan; else - phy_default = list_entry(&tp->first_phy, + phy_default = list_first_entry(&tp->first_phy, struct sis190_phy, list); } -- cgit v0.10.2 From 4bb3f522077f6b10531451f50c376d6137ebd364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20romieu?= Date: Wed, 17 Jun 2009 11:41:45 +0000 Subject: r8169: do not bring device down when suspending Stopping all activity through ChipCmd and blindly acking the irqs is neither nice nor completely needed: the transition to low-power mode does enough work and it apparently keeps the device in a sane state. Patch suggested by a fix for http://bugzilla.kernel.org/show_bug.cgi?id=9512 The rtl_shutdown path is kept unchanged so far. Signed-off-by: Francois Romieu Tested-by: Anders Eriksson Cc: Edward Hsu Signed-off-by: David S. Miller diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 35196fa..4e22462 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -3811,22 +3811,11 @@ static struct net_device_stats *rtl8169_get_stats(struct net_device *dev) static void rtl8169_net_suspend(struct net_device *dev) { - struct rtl8169_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - if (!netif_running(dev)) return; netif_device_detach(dev); netif_stop_queue(dev); - - spin_lock_irq(&tp->lock); - - rtl8169_asic_down(ioaddr); - - rtl8169_rx_missed(dev, ioaddr); - - spin_unlock_irq(&tp->lock); } #ifdef CONFIG_PM @@ -3876,9 +3865,17 @@ static struct dev_pm_ops rtl8169_pm_ops = { static void rtl_shutdown(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; rtl8169_net_suspend(dev); + spin_lock_irq(&tp->lock); + + rtl8169_asic_down(ioaddr); + + spin_unlock_irq(&tp->lock); + if (system_state == SYSTEM_POWER_OFF) { pci_wake_from_d3(pdev, true); pci_set_power_state(pdev, PCI_D3hot); -- cgit v0.10.2 From d104acaf0540aee527d22eafd14b0a3d30be7f81 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:32 +0000 Subject: sky2: turn off pause during shutdown This unblocks the chip if it is stuck in pause cycle during shutdown. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index c6ceba9..5de2a90 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1808,7 +1808,8 @@ static int sky2_down(struct net_device *dev) synchronize_irq(hw->pdev->irq); - sky2_gmac_reset(hw, port); + /* Force flow control off */ + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); /* Stop transmitter */ sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP); -- cgit v0.10.2 From c0bad0f2e4366d5bbfe0c4a7a80bca8f4b05272b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:33 +0000 Subject: sky2: more receive shutdown Reset more parts of the receive path when device is take offline. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 5de2a90..e4e24ee 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1151,7 +1151,14 @@ stopped: /* reset the Rx prefetch unit */ sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); - mmiowb(); + + /* Reset the RAM Buffer receive queue */ + sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_RST_SET); + + /* Reset Rx MAC FIFO */ + sky2_write8(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), GMF_RST_SET); + + sky2_read8(hw, B0_CTST); } /* Clean out receive buffer area, assumes receiver hardware stopped */ -- cgit v0.10.2 From 1fd82f3cafa8e7854db08eccbdb8a9218225e1ef Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:34 +0000 Subject: sky2: PCI irq issues Add some read's to avoid any PCI posting issues when controlling irq's. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index e4e24ee..cc1c8d1 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1495,6 +1495,7 @@ static int sky2_up(struct net_device *dev) imask = sky2_read32(hw, B0_IMSK); imask |= portirq_msk[port]; sky2_write32(hw, B0_IMSK, imask); + sky2_read32(hw, B0_IMSK); sky2_set_multicast(dev); @@ -1812,6 +1813,7 @@ static int sky2_down(struct net_device *dev) imask = sky2_read32(hw, B0_IMSK); imask &= ~portirq_msk[port]; sky2_write32(hw, B0_IMSK, imask); + sky2_read32(hw, B0_IMSK); synchronize_irq(hw->pdev->irq); -- cgit v0.10.2 From 6c83504ff24f4a6bf28ad865e7c0619b17349e08 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:35 +0000 Subject: sky2: fix shutdown synchronization The logic in sky2_down was incorrect. Receiver could report status after rx_stop was called. The steps need to be: * stop new frames from being transmitted * shut off transmit/receive logic * synchronize with NAPI to process status info about transmitter and receiver Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index cc1c8d1..fd7accf 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1815,8 +1815,6 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, B0_IMSK, imask); sky2_read32(hw, B0_IMSK); - synchronize_irq(hw->pdev->irq); - /* Force flow control off */ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); @@ -1831,9 +1829,6 @@ static int sky2_down(struct net_device *dev) ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA); gma_write16(hw, port, GM_GP_CTRL, ctrl); - /* Make sure no packets are pending */ - napi_synchronize(&hw->napi); - sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); /* Workaround shared GMAC reset */ @@ -1864,6 +1859,15 @@ static int sky2_down(struct net_device *dev) sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET); + /* Force any delayed status interrrupt and NAPI */ + sky2_write32(hw, STAT_LEV_TIMER_CNT, 0); + sky2_write32(hw, STAT_TX_TIMER_CNT, 0); + sky2_write32(hw, STAT_ISR_TIMER_CNT, 0); + sky2_read8(hw, STAT_ISR_TIMER_CTRL); + + synchronize_irq(hw->pdev->irq); + napi_synchronize(&hw->napi); + sky2_phy_power_down(hw, port); /* turn off LED's */ -- cgit v0.10.2 From bf15fe996e050332c018cf63dd51288b081cc556 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:36 +0000 Subject: sky2: receive counter update Since it is likely that there are multiple packets received per interrupt, only update the receive counters once after all packets are processed. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index fd7accf..1b8de45 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2357,11 +2357,25 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) } } +static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port, + unsigned packets, unsigned bytes) +{ + if (packets) { + struct net_device *dev = hw->dev[port]; + + dev->stats.rx_packets += packets; + dev->stats.rx_bytes += bytes; + dev->last_rx = jiffies; + sky2_rx_update(netdev_priv(dev), rxqaddr[port]); + } +} + /* Process status response ring */ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) { int work_done = 0; - unsigned rx[2] = { 0, 0 }; + unsigned int total_bytes[2] = { 0 }; + unsigned int total_packets[2] = { 0 }; rmb(); do { @@ -2388,7 +2402,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) le->opcode = 0; switch (opcode & ~HW_OWNER) { case OP_RXSTAT: - ++rx[port]; + total_packets[port]++; + total_bytes[port] += length; skb = sky2_receive(dev, length, status); if (unlikely(!skb)) { dev->stats.rx_dropped++; @@ -2406,9 +2421,6 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) } skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - dev->last_rx = jiffies; #ifdef SKY2_VLAN_TAG_USED if (sky2->vlgrp && (status & GMR_FS_VLAN)) { @@ -2487,11 +2499,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); exit_loop: - if (rx[0]) - sky2_rx_update(netdev_priv(hw->dev[0]), Q_R1); - - if (rx[1]) - sky2_rx_update(netdev_priv(hw->dev[1]), Q_R2); + sky2_rx_done(hw, 0, total_packets[0], total_bytes[0]); + sky2_rx_done(hw, 1, total_packets[1], total_bytes[1]); return work_done; } -- cgit v0.10.2 From e9c1be80a7403fb817ec6229ec20a39e377cc4ce Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:37 +0000 Subject: sky2: reduce default transmit ring Reduce the size of the driver transmit ring to reduce latency and allow qdisc to do better rate control. Also make it obvious what the minimum transmit ring allowed is and why. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1b8de45..6e95d2f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -65,9 +65,9 @@ #define RX_DEF_PENDING RX_MAX_PENDING #define TX_RING_SIZE 512 -#define TX_DEF_PENDING (TX_RING_SIZE - 1) -#define TX_MIN_PENDING 64 +#define TX_DEF_PENDING 128 #define MAX_SKB_TX_LE (4 + (sizeof(dma_addr_t)/sizeof(u32))*MAX_SKB_FRAGS) +#define TX_MIN_PENDING (MAX_SKB_TX_LE+1) #define STATUS_RING_SIZE 2048 /* 2 ports * (TX + 2*RX) */ #define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le)) -- cgit v0.10.2 From bd1c6869f14f88aa82587ff51303e72dc7eec30e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:38 +0000 Subject: sky2: skb recycling This patch implements skb recycling. It reclaims transmitted skb's for use in the receive ring. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6e95d2f..4f2afc7 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1176,6 +1176,7 @@ static void sky2_rx_clean(struct sky2_port *sky2) re->skb = NULL; } } + skb_queue_purge(&sky2->rx_recycle); } /* Basic MII support */ @@ -1252,6 +1253,12 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp } #endif +/* Amount of required worst case padding in rx buffer */ +static inline unsigned sky2_rx_pad(const struct sky2_hw *hw) +{ + return (hw->flags & SKY2_HW_RAM_BUFFER) ? 8 : 2; +} + /* * Allocate an skb for receiving. If the MTU is large enough * make the skb non-linear with a fragment list of pages. @@ -1261,6 +1268,13 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2) struct sk_buff *skb; int i; + skb = __skb_dequeue(&sky2->rx_recycle); + if (!skb) + skb = netdev_alloc_skb(sky2->netdev, sky2->rx_data_size + + sky2_rx_pad(sky2->hw)); + if (!skb) + goto nomem; + if (sky2->hw->flags & SKY2_HW_RAM_BUFFER) { unsigned char *start; /* @@ -1269,18 +1283,10 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2) * The buffer returned from netdev_alloc_skb is * aligned except if slab debugging is enabled. */ - skb = netdev_alloc_skb(sky2->netdev, sky2->rx_data_size + 8); - if (!skb) - goto nomem; start = PTR_ALIGN(skb->data, 8); skb_reserve(skb, start - skb->data); - } else { - skb = netdev_alloc_skb(sky2->netdev, - sky2->rx_data_size + NET_IP_ALIGN); - if (!skb) - goto nomem; + } else skb_reserve(skb, NET_IP_ALIGN); - } for (i = 0; i < sky2->rx_nfrags; i++) { struct page *page = alloc_page(GFP_ATOMIC); @@ -1357,6 +1363,8 @@ static int sky2_rx_start(struct sky2_port *sky2) sky2->rx_data_size = size; + skb_queue_head_init(&sky2->rx_recycle); + /* Fill Rx ring */ for (i = 0; i < sky2->rx_pending; i++) { re = sky2->rx_ring + i; @@ -1764,14 +1772,22 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) } if (le->ctrl & EOP) { + struct sk_buff *skb = re->skb; + if (unlikely(netif_msg_tx_done(sky2))) printk(KERN_DEBUG "%s: tx done %u\n", dev->name, idx); dev->stats.tx_packets++; - dev->stats.tx_bytes += re->skb->len; + dev->stats.tx_bytes += skb->len; + + if (skb_queue_len(&sky2->rx_recycle) < sky2->rx_pending + && skb_recycle_check(skb, sky2->rx_data_size + + sky2_rx_pad(sky2->hw))) + __skb_queue_head(&sky2->rx_recycle, skb); + else + dev_kfree_skb_any(skb); - dev_kfree_skb_any(re->skb); sky2->tx_next = RING_NEXT(idx, TX_RING_SIZE); } } diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 92fb24b..b5549c9 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -2028,6 +2028,7 @@ struct sky2_port { u16 rx_pending; u16 rx_data_size; u16 rx_nfrags; + struct sk_buff_head rx_recycle; #ifdef SKY2_VLAN_TAG_USED u16 rx_tag; -- cgit v0.10.2 From 37e5a2439b43bb90655e17f00c9db5759909a712 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:39 +0000 Subject: sky2: add GRO support Add support for generic receive offload. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 4f2afc7..d2112e6 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2373,6 +2373,26 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) } } +static inline void sky2_skb_rx(const struct sky2_port *sky2, + u32 status, struct sk_buff *skb) +{ +#ifdef SKY2_VLAN_TAG_USED + u16 vlan_tag = be16_to_cpu(sky2->rx_tag); + if (sky2->vlgrp && (status & GMR_FS_VLAN)) { + if (skb->ip_summed == CHECKSUM_NONE) + vlan_hwaccel_receive_skb(skb, sky2->vlgrp, vlan_tag); + else + vlan_gro_receive(&sky2->hw->napi, sky2->vlgrp, + vlan_tag, skb); + return; + } +#endif + if (skb->ip_summed == CHECKSUM_NONE) + netif_receive_skb(skb); + else + napi_gro_receive(&sky2->hw->napi, skb); +} + static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port, unsigned packets, unsigned bytes) { @@ -2438,14 +2458,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) skb->protocol = eth_type_trans(skb, dev); -#ifdef SKY2_VLAN_TAG_USED - if (sky2->vlgrp && (status & GMR_FS_VLAN)) { - vlan_hwaccel_receive_skb(skb, - sky2->vlgrp, - be16_to_cpu(sky2->rx_tag)); - } else -#endif - netif_receive_skb(skb); + sky2_skb_rx(sky2, status, skb); /* Stop after net poll weight */ if (++work_done >= to_do) -- cgit v0.10.2 From e4f1482e686212e6d1dd6df93888bb26344981c6 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 17 Jun 2009 07:30:40 +0000 Subject: sky2: version 1.23 Version bump. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d2112e6..7681d28 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -50,7 +50,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.22" +#define DRV_VERSION "1.23" #define PFX DRV_NAME " " /* -- cgit v0.10.2 From b964758050f856e44f5fe645d03bea8a1b0b66bd Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Tue, 16 Jun 2009 08:33:55 +0000 Subject: pkt_sched: Update drops stats in act_police Action police statistics could be misleading because drops are not shown when expected. With feedback from: Jamal Hadi Salim Reported-by: Pawel Staszewski Signed-off-by: Jarek Poplawski Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller diff --git a/net/sched/act_police.c b/net/sched/act_police.c index f8f047b6..723964c 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -294,6 +294,8 @@ static int tcf_act_police(struct sk_buff *skb, struct tc_action *a, if (police->tcfp_ewma_rate && police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { police->tcf_qstats.overlimits++; + if (police->tcf_action == TC_ACT_SHOT) + police->tcf_qstats.drops++; spin_unlock(&police->tcf_lock); return police->tcf_action; } @@ -327,6 +329,8 @@ static int tcf_act_police(struct sk_buff *skb, struct tc_action *a, } police->tcf_qstats.overlimits++; + if (police->tcf_action == TC_ACT_SHOT) + police->tcf_qstats.drops++; spin_unlock(&police->tcf_lock); return police->tcf_action; } -- cgit v0.10.2 From 7b85576d15bf2574b0a451108f59f9ad4170dd3f Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Thu, 18 Jun 2009 00:28:51 -0700 Subject: ipv4: Fix fib_trie rebalancing, part 2 My previous patch, which explicitly delays freeing of tnodes by adding them to the list to flush them after the update is finished, isn't strict enough. It treats exceptionally tnodes without parent, assuming they are newly created, so "invisible" for the read side yet. But the top tnode doesn't have parent as well, so we have to exclude all exceptions (at least until a better way is found). Additionally we need to move rcu assignment of this node before flushing, so the return type of the trie_rebalance() function is changed. Reported-by: Yan Zheng Signed-off-by: Jarek Poplawski Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index d1a39b1..012cf5a 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -391,13 +391,8 @@ static inline void tnode_free(struct tnode *tn) static void tnode_free_safe(struct tnode *tn) { BUG_ON(IS_LEAF(tn)); - - if (node_parent((struct node *) tn)) { - tn->tnode_free = tnode_free_head; - tnode_free_head = tn; - } else { - tnode_free(tn); - } + tn->tnode_free = tnode_free_head; + tnode_free_head = tn; } static void tnode_free_flush(void) @@ -1009,7 +1004,7 @@ fib_find_node(struct trie *t, u32 key) return NULL; } -static struct node *trie_rebalance(struct trie *t, struct tnode *tn) +static void trie_rebalance(struct trie *t, struct tnode *tn) { int wasfull; t_key cindex, key; @@ -1033,12 +1028,13 @@ static struct node *trie_rebalance(struct trie *t, struct tnode *tn) } /* Handle last (top) tnode */ - if (IS_TNODE(tn)) { + if (IS_TNODE(tn)) tn = (struct tnode *)resize(t, (struct tnode *)tn); - tnode_free_flush(); - } - return (struct node *)tn; + rcu_assign_pointer(t->trie, (struct node *)tn); + tnode_free_flush(); + + return; } /* only used from updater-side */ @@ -1186,7 +1182,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) /* Rebalance the trie */ - rcu_assign_pointer(t->trie, trie_rebalance(t, tp)); + trie_rebalance(t, tp); done: return fa_head; } @@ -1605,7 +1601,7 @@ static void trie_leaf_remove(struct trie *t, struct leaf *l) if (tp) { t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits); put_child(t, (struct tnode *)tp, cindex, NULL); - rcu_assign_pointer(t->trie, trie_rebalance(t, tp)); + trie_rebalance(t, tp); } else rcu_assign_pointer(t->trie, NULL); -- cgit v0.10.2 From 31278e71471399beaff9280737e52b47db4dc345 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 17 Jun 2009 01:12:19 +0000 Subject: net: group address list and its count This patch is inspired by patch recently posted by Johannes Berg. Basically what my patch does is to group list and a count of addresses into newly introduced structure netdev_hw_addr_list. This brings us two benefits: 1) struct net_device becames a bit nicer. 2) in the future there will be a possibility to operate with lists independently on netdevices (with exporting right functions). I wanted to introduce this patch before I'll post a multicast lists conversion. Signed-off-by: Jiri Pirko drivers/net/bnx2.c | 4 +- drivers/net/e1000/e1000_main.c | 4 +- drivers/net/ixgbe/ixgbe_main.c | 6 +- drivers/net/mv643xx_eth.c | 2 +- drivers/net/niu.c | 4 +- drivers/net/virtio_net.c | 10 ++-- drivers/s390/net/qeth_l2_main.c | 2 +- include/linux/netdevice.h | 17 +++-- net/core/dev.c | 130 ++++++++++++++++++-------------------- 9 files changed, 89 insertions(+), 90 deletions(-) Signed-off-by: David S. Miller diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 7e37381..38f1c33 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -3552,14 +3552,14 @@ bnx2_set_rx_mode(struct net_device *dev) sort_mode |= BNX2_RPM_SORT_USER0_MC_HSH_EN; } - if (dev->uc_count > BNX2_MAX_UNICAST_ADDRESSES) { + if (dev->uc.count > BNX2_MAX_UNICAST_ADDRESSES) { rx_mode |= BNX2_EMAC_RX_MODE_PROMISCUOUS; sort_mode |= BNX2_RPM_SORT_USER0_PROM_EN | BNX2_RPM_SORT_USER0_PROM_VLAN; } else if (!(dev->flags & IFF_PROMISC)) { /* Add all entries into to the match filter list */ i = 0; - list_for_each_entry(ha, &dev->uc_list, list) { + list_for_each_entry(ha, &dev->uc.list, list) { bnx2_set_mac_addr(bp, ha->addr, i + BNX2_START_UNICAST_ADDRESS_INDEX); sort_mode |= (1 << diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 8d36743..5e3356f 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -2370,7 +2370,7 @@ static void e1000_set_rx_mode(struct net_device *netdev) rctl |= E1000_RCTL_VFE; } - if (netdev->uc_count > rar_entries - 1) { + if (netdev->uc.count > rar_entries - 1) { rctl |= E1000_RCTL_UPE; } else if (!(netdev->flags & IFF_PROMISC)) { rctl &= ~E1000_RCTL_UPE; @@ -2394,7 +2394,7 @@ static void e1000_set_rx_mode(struct net_device *netdev) */ i = 1; if (use_uc) - list_for_each_entry(ha, &netdev->uc_list, list) { + list_for_each_entry(ha, &netdev->uc.list, list) { if (i == rar_entries) break; e1000_rar_set(hw, ha->addr, i++); diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index a551a96..e756e22 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -2321,7 +2321,7 @@ static void ixgbe_set_rx_mode(struct net_device *netdev) IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl); /* reprogram secondary unicast list */ - hw->mac.ops.update_uc_addr_list(hw, &netdev->uc_list); + hw->mac.ops.update_uc_addr_list(hw, &netdev->uc.list); /* reprogram multicast list */ addr_count = netdev->mc_count; @@ -5261,7 +5261,7 @@ static int ixgbe_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) /** * ixgbe_add_sanmac_netdev - Add the SAN MAC address to the corresponding - * netdev->dev_addr_list + * netdev->dev_addrs * @netdev: network interface device structure * * Returns non-zero on failure @@ -5282,7 +5282,7 @@ static int ixgbe_add_sanmac_netdev(struct net_device *dev) /** * ixgbe_del_sanmac_netdev - Removes the SAN MAC address to the corresponding - * netdev->dev_addr_list + * netdev->dev_addrs * @netdev: network interface device structure * * Returns non-zero on failure diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index b4e18a5..745ae8b4 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1729,7 +1729,7 @@ static u32 uc_addr_filter_mask(struct net_device *dev) return 0; nibbles = 1 << (dev->dev_addr[5] & 0x0f); - list_for_each_entry(ha, &dev->uc_list, list) { + list_for_each_entry(ha, &dev->uc.list, list) { if (memcmp(dev->dev_addr, ha->addr, 5)) return 0; if ((dev->dev_addr[5] ^ ha->addr[5]) & 0xf0) diff --git a/drivers/net/niu.c b/drivers/net/niu.c index fa61a12..d2146d4 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -6376,7 +6376,7 @@ static void niu_set_rx_mode(struct net_device *dev) if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 0)) np->flags |= NIU_FLAGS_MCAST; - alt_cnt = dev->uc_count; + alt_cnt = dev->uc.count; if (alt_cnt > niu_num_alt_addr(np)) { alt_cnt = 0; np->flags |= NIU_FLAGS_PROMISC; @@ -6385,7 +6385,7 @@ static void niu_set_rx_mode(struct net_device *dev) if (alt_cnt) { int index = 0; - list_for_each_entry(ha, &dev->uc_list, list) { + list_for_each_entry(ha, &dev->uc.list, list) { err = niu_set_alt_mac(np, index, ha->addr); if (err) printk(KERN_WARNING PFX "%s: Error %d " diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 52198f6..2a6e81d 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -709,7 +709,7 @@ static void virtnet_set_rx_mode(struct net_device *dev) allmulti ? "en" : "dis"); /* MAC filter - use one buffer for both lists */ - mac_data = buf = kzalloc(((dev->uc_count + dev->mc_count) * ETH_ALEN) + + mac_data = buf = kzalloc(((dev->uc.count + dev->mc_count) * ETH_ALEN) + (2 * sizeof(mac_data->entries)), GFP_ATOMIC); if (!buf) { dev_warn(&dev->dev, "No memory for MAC address buffer\n"); @@ -719,16 +719,16 @@ static void virtnet_set_rx_mode(struct net_device *dev) sg_init_table(sg, 2); /* Store the unicast list and count in the front of the buffer */ - mac_data->entries = dev->uc_count; + mac_data->entries = dev->uc.count; i = 0; - list_for_each_entry(ha, &dev->uc_list, list) + list_for_each_entry(ha, &dev->uc.list, list) memcpy(&mac_data->macs[i++][0], ha->addr, ETH_ALEN); sg_set_buf(&sg[0], mac_data, - sizeof(mac_data->entries) + (dev->uc_count * ETH_ALEN)); + sizeof(mac_data->entries) + (dev->uc.count * ETH_ALEN)); /* multicast list and count fill the end */ - mac_data = (void *)&mac_data->macs[dev->uc_count][0]; + mac_data = (void *)&mac_data->macs[dev->uc.count][0]; mac_data->entries = dev->mc_count; addr = dev->mc_list; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ecd3d06..3607d10 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -655,7 +655,7 @@ static void qeth_l2_set_multicast_list(struct net_device *dev) for (dm = dev->mc_list; dm; dm = dm->next) qeth_l2_add_mc(card, dm->da_addr, 0); - list_for_each_entry(ha, &dev->uc_list, list) + list_for_each_entry(ha, &dev->uc.list, list) qeth_l2_add_mc(card, ha->addr, 1); spin_unlock_bh(&card->mclock); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9ea8d6d..d4a4d98 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -224,6 +224,11 @@ struct netdev_hw_addr { struct rcu_head rcu_head; }; +struct netdev_hw_addr_list { + struct list_head list; + int count; +}; + struct hh_cache { struct hh_cache *hh_next; /* Next entry */ @@ -776,9 +781,8 @@ struct net_device unsigned char addr_len; /* hardware address length */ unsigned short dev_id; /* for shared network cards */ - struct list_head uc_list; /* Secondary unicast mac - addresses */ - int uc_count; /* Number of installed ucasts */ + struct netdev_hw_addr_list uc; /* Secondary unicast + mac addresses */ int uc_promisc; spinlock_t addr_list_lock; struct dev_addr_list *mc_list; /* Multicast mac addresses */ @@ -810,7 +814,8 @@ struct net_device because most packets are unicast) */ - struct list_head dev_addr_list; /* list of device hw addresses */ + struct netdev_hw_addr_list dev_addrs; /* list of device + hw addresses */ unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ @@ -1806,11 +1811,11 @@ static inline void netif_addr_unlock_bh(struct net_device *dev) } /* - * dev_addr_list walker. Should be used only for read access. Call with + * dev_addrs walker. Should be used only for read access. Call with * rcu_read_lock held. */ #define for_each_dev_addr(dev, ha) \ - list_for_each_entry_rcu(ha, &dev->dev_addr_list, list) + list_for_each_entry_rcu(ha, &dev->dev_addrs.list, list) /* These functions live elsewhere (drivers/net/net_init.c, but related) */ diff --git a/net/core/dev.c b/net/core/dev.c index 576a615..baf2dc1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3461,10 +3461,10 @@ void __dev_set_rx_mode(struct net_device *dev) /* Unicast addresses changes may only happen under the rtnl, * therefore calling __dev_set_promiscuity here is safe. */ - if (dev->uc_count > 0 && !dev->uc_promisc) { + if (dev->uc.count > 0 && !dev->uc_promisc) { __dev_set_promiscuity(dev, 1); dev->uc_promisc = 1; - } else if (dev->uc_count == 0 && dev->uc_promisc) { + } else if (dev->uc.count == 0 && dev->uc_promisc) { __dev_set_promiscuity(dev, -1); dev->uc_promisc = 0; } @@ -3483,9 +3483,8 @@ void dev_set_rx_mode(struct net_device *dev) /* hw addresses list handling functions */ -static int __hw_addr_add(struct list_head *list, int *delta, - unsigned char *addr, int addr_len, - unsigned char addr_type) +static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, + int addr_len, unsigned char addr_type) { struct netdev_hw_addr *ha; int alloc_size; @@ -3493,7 +3492,7 @@ static int __hw_addr_add(struct list_head *list, int *delta, if (addr_len > MAX_ADDR_LEN) return -EINVAL; - list_for_each_entry(ha, list, list) { + list_for_each_entry(ha, &list->list, list) { if (!memcmp(ha->addr, addr, addr_len) && ha->type == addr_type) { ha->refcount++; @@ -3512,9 +3511,8 @@ static int __hw_addr_add(struct list_head *list, int *delta, ha->type = addr_type; ha->refcount = 1; ha->synced = false; - list_add_tail_rcu(&ha->list, list); - if (delta) - (*delta)++; + list_add_tail_rcu(&ha->list, &list->list); + list->count++; return 0; } @@ -3526,120 +3524,121 @@ static void ha_rcu_free(struct rcu_head *head) kfree(ha); } -static int __hw_addr_del(struct list_head *list, int *delta, - unsigned char *addr, int addr_len, - unsigned char addr_type) +static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr, + int addr_len, unsigned char addr_type) { struct netdev_hw_addr *ha; - list_for_each_entry(ha, list, list) { + list_for_each_entry(ha, &list->list, list) { if (!memcmp(ha->addr, addr, addr_len) && (ha->type == addr_type || !addr_type)) { if (--ha->refcount) return 0; list_del_rcu(&ha->list); call_rcu(&ha->rcu_head, ha_rcu_free); - if (delta) - (*delta)--; + list->count--; return 0; } } return -ENOENT; } -static int __hw_addr_add_multiple(struct list_head *to_list, int *to_delta, - struct list_head *from_list, int addr_len, +static int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type) { int err; struct netdev_hw_addr *ha, *ha2; unsigned char type; - list_for_each_entry(ha, from_list, list) { + list_for_each_entry(ha, &from_list->list, list) { type = addr_type ? addr_type : ha->type; - err = __hw_addr_add(to_list, to_delta, ha->addr, - addr_len, type); + err = __hw_addr_add(to_list, ha->addr, addr_len, type); if (err) goto unroll; } return 0; unroll: - list_for_each_entry(ha2, from_list, list) { + list_for_each_entry(ha2, &from_list->list, list) { if (ha2 == ha) break; type = addr_type ? addr_type : ha2->type; - __hw_addr_del(to_list, to_delta, ha2->addr, - addr_len, type); + __hw_addr_del(to_list, ha2->addr, addr_len, type); } return err; } -static void __hw_addr_del_multiple(struct list_head *to_list, int *to_delta, - struct list_head *from_list, int addr_len, +static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type) { struct netdev_hw_addr *ha; unsigned char type; - list_for_each_entry(ha, from_list, list) { + list_for_each_entry(ha, &from_list->list, list) { type = addr_type ? addr_type : ha->type; - __hw_addr_del(to_list, to_delta, ha->addr, - addr_len, addr_type); + __hw_addr_del(to_list, ha->addr, addr_len, addr_type); } } -static int __hw_addr_sync(struct list_head *to_list, int *to_delta, - struct list_head *from_list, int *from_delta, +static int __hw_addr_sync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, int addr_len) { int err = 0; struct netdev_hw_addr *ha, *tmp; - list_for_each_entry_safe(ha, tmp, from_list, list) { + list_for_each_entry_safe(ha, tmp, &from_list->list, list) { if (!ha->synced) { - err = __hw_addr_add(to_list, to_delta, ha->addr, + err = __hw_addr_add(to_list, ha->addr, addr_len, ha->type); if (err) break; ha->synced = true; ha->refcount++; } else if (ha->refcount == 1) { - __hw_addr_del(to_list, to_delta, ha->addr, - addr_len, ha->type); - __hw_addr_del(from_list, from_delta, ha->addr, - addr_len, ha->type); + __hw_addr_del(to_list, ha->addr, addr_len, ha->type); + __hw_addr_del(from_list, ha->addr, addr_len, ha->type); } } return err; } -static void __hw_addr_unsync(struct list_head *to_list, int *to_delta, - struct list_head *from_list, int *from_delta, +static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, int addr_len) { struct netdev_hw_addr *ha, *tmp; - list_for_each_entry_safe(ha, tmp, from_list, list) { + list_for_each_entry_safe(ha, tmp, &from_list->list, list) { if (ha->synced) { - __hw_addr_del(to_list, to_delta, ha->addr, + __hw_addr_del(to_list, ha->addr, addr_len, ha->type); ha->synced = false; - __hw_addr_del(from_list, from_delta, ha->addr, + __hw_addr_del(from_list, ha->addr, addr_len, ha->type); } } } - -static void __hw_addr_flush(struct list_head *list) +static void __hw_addr_flush(struct netdev_hw_addr_list *list) { struct netdev_hw_addr *ha, *tmp; - list_for_each_entry_safe(ha, tmp, list, list) { + list_for_each_entry_safe(ha, tmp, &list->list, list) { list_del_rcu(&ha->list); call_rcu(&ha->rcu_head, ha_rcu_free); } + list->count = 0; +} + +static void __hw_addr_init(struct netdev_hw_addr_list *list) +{ + INIT_LIST_HEAD(&list->list); + list->count = 0; } /* Device addresses handling functions */ @@ -3648,7 +3647,7 @@ static void dev_addr_flush(struct net_device *dev) { /* rtnl_mutex must be held here */ - __hw_addr_flush(&dev->dev_addr_list); + __hw_addr_flush(&dev->dev_addrs); dev->dev_addr = NULL; } @@ -3660,16 +3659,16 @@ static int dev_addr_init(struct net_device *dev) /* rtnl_mutex must be held here */ - INIT_LIST_HEAD(&dev->dev_addr_list); + __hw_addr_init(&dev->dev_addrs); memset(addr, 0, sizeof(addr)); - err = __hw_addr_add(&dev->dev_addr_list, NULL, addr, sizeof(addr), + err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr), NETDEV_HW_ADDR_T_LAN); if (!err) { /* * Get the first (previously created) address from the list * and set dev_addr pointer to this location. */ - ha = list_first_entry(&dev->dev_addr_list, + ha = list_first_entry(&dev->dev_addrs.list, struct netdev_hw_addr, list); dev->dev_addr = ha->addr; } @@ -3694,8 +3693,7 @@ int dev_addr_add(struct net_device *dev, unsigned char *addr, ASSERT_RTNL(); - err = __hw_addr_add(&dev->dev_addr_list, NULL, addr, dev->addr_len, - addr_type); + err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type); if (!err) call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); return err; @@ -3725,11 +3723,12 @@ int dev_addr_del(struct net_device *dev, unsigned char *addr, * We can not remove the first address from the list because * dev->dev_addr points to that. */ - ha = list_first_entry(&dev->dev_addr_list, struct netdev_hw_addr, list); + ha = list_first_entry(&dev->dev_addrs.list, + struct netdev_hw_addr, list); if (ha->addr == dev->dev_addr && ha->refcount == 1) return -ENOENT; - err = __hw_addr_del(&dev->dev_addr_list, NULL, addr, dev->addr_len, + err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len, addr_type); if (!err) call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); @@ -3757,8 +3756,7 @@ int dev_addr_add_multiple(struct net_device *to_dev, if (from_dev->addr_len != to_dev->addr_len) return -EINVAL; - err = __hw_addr_add_multiple(&to_dev->dev_addr_list, NULL, - &from_dev->dev_addr_list, + err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs, to_dev->addr_len, addr_type); if (!err) call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev); @@ -3784,15 +3782,14 @@ int dev_addr_del_multiple(struct net_device *to_dev, if (from_dev->addr_len != to_dev->addr_len) return -EINVAL; - __hw_addr_del_multiple(&to_dev->dev_addr_list, NULL, - &from_dev->dev_addr_list, + __hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs, to_dev->addr_len, addr_type); call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev); return 0; } EXPORT_SYMBOL(dev_addr_del_multiple); -/* unicast and multicast addresses handling functions */ +/* multicast addresses handling functions */ int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int glbl) @@ -3868,8 +3865,8 @@ int dev_unicast_delete(struct net_device *dev, void *addr) ASSERT_RTNL(); - err = __hw_addr_del(&dev->uc_list, &dev->uc_count, addr, - dev->addr_len, NETDEV_HW_ADDR_T_UNICAST); + err = __hw_addr_del(&dev->uc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); if (!err) __dev_set_rx_mode(dev); return err; @@ -3892,8 +3889,8 @@ int dev_unicast_add(struct net_device *dev, void *addr) ASSERT_RTNL(); - err = __hw_addr_add(&dev->uc_list, &dev->uc_count, addr, - dev->addr_len, NETDEV_HW_ADDR_T_UNICAST); + err = __hw_addr_add(&dev->uc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); if (!err) __dev_set_rx_mode(dev); return err; @@ -3966,8 +3963,7 @@ int dev_unicast_sync(struct net_device *to, struct net_device *from) if (to->addr_len != from->addr_len) return -EINVAL; - err = __hw_addr_sync(&to->uc_list, &to->uc_count, - &from->uc_list, &from->uc_count, to->addr_len); + err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len); if (!err) __dev_set_rx_mode(to); return err; @@ -3990,8 +3986,7 @@ void dev_unicast_unsync(struct net_device *to, struct net_device *from) if (to->addr_len != from->addr_len) return; - __hw_addr_unsync(&to->uc_list, &to->uc_count, - &from->uc_list, &from->uc_count, to->addr_len); + __hw_addr_unsync(&to->uc, &from->uc, to->addr_len); __dev_set_rx_mode(to); } EXPORT_SYMBOL(dev_unicast_unsync); @@ -4000,15 +3995,14 @@ static void dev_unicast_flush(struct net_device *dev) { /* rtnl_mutex must be held here */ - __hw_addr_flush(&dev->uc_list); - dev->uc_count = 0; + __hw_addr_flush(&dev->uc); } static void dev_unicast_init(struct net_device *dev) { /* rtnl_mutex must be held here */ - INIT_LIST_HEAD(&dev->uc_list); + __hw_addr_init(&dev->uc); } -- cgit v0.10.2 From ab0a8e6c663047363e7436621b66c2ae575d2525 Mon Sep 17 00:00:00 2001 From: "chaithrika@ti.com" Date: Wed, 17 Jun 2009 01:28:54 +0000 Subject: TI DaVinci EMAC : Fix rmmod error clk_disable was called twice in the remove function. Correct this so that the driver module unloads without error. Signed-off-by: Chaithrika U S Signed-off-by: David S. Miller diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 0e9b9f9..2df8fb0 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -2767,7 +2767,6 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev) dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n"); - clk_disable(emac_clk); platform_set_drvdata(pdev, NULL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mdiobus_unregister(priv->mii_bus); -- cgit v0.10.2 From 6905b1f1a03a48dcf115a2927f7b87dba8d5e566 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 14 Jun 2009 09:46:46 +0000 Subject: Net / e100: Fix suspend of devices that cannot be power managed If the adapter is not power-manageable using either ACPI, or the native PCI PM interface, __e100_power_off() returns error code, which causes every attempt to suspend to fail, although it should return 0 in such a case. Fix this problem by ignoring the return value of pci_set_power_state() in __e100_power_off(). Signed-off-by: Rafael J. Wysocki Acked-by: Andreas Mohr Signed-off-by: David S. Miller diff --git a/drivers/net/e100.c b/drivers/net/e100.c index f7929e8..efa680f 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -2895,12 +2895,13 @@ static void __e100_shutdown(struct pci_dev *pdev, bool *enable_wake) static int __e100_power_off(struct pci_dev *pdev, bool wake) { - if (wake) { + if (wake) return pci_prepare_to_sleep(pdev); - } else { - pci_wake_from_d3(pdev, false); - return pci_set_power_state(pdev, PCI_D3hot); - } + + pci_wake_from_d3(pdev, false); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; } #ifdef CONFIG_PM -- cgit v0.10.2 From d3b238a03efd6d644ff93c8b10a1d38a596f2e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9guier=20R=C3=A9gis?= Date: Tue, 16 Jun 2009 11:25:49 +0000 Subject: via-velocity : fix no link detection on boot on boot, link is always up. Signed-off-by: Seguier Regis Signed-off-by: David S. Miller diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index e2a7725..b02f7ad 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -989,8 +989,10 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi if (ret < 0) goto err_iounmap; - if (velocity_get_link(dev)) + if (!velocity_get_link(dev)) { netif_carrier_off(dev); + vptr->mii_status |= VELOCITY_LINK_FAIL; + } velocity_print_info(vptr); pci_set_drvdata(pdev, dev); -- cgit v0.10.2 From 31e6d363abcd0d05766c82f1a9c905a4c974a199 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 17 Jun 2009 19:05:41 -0700 Subject: net: correct off-by-one write allocations reports commit 2b85a34e911bf483c27cfdd124aeb1605145dc80 (net: No more expensive sock_hold()/sock_put() on each tx) changed initial sk_wmem_alloc value. We need to take into account this offset when reporting sk_wmem_alloc to user, in PROC_FS files or various ioctls (SIOCOUTQ/TIOCOUTQ) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c index fd8e084..80caad1 100644 --- a/net/appletalk/atalk_proc.c +++ b/net/appletalk/atalk_proc.c @@ -204,8 +204,8 @@ static int atalk_seq_socket_show(struct seq_file *seq, void *v) "%02X %d\n", s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, ntohs(at->dest_net), at->dest_node, at->dest_port, - atomic_read(&s->sk_wmem_alloc), - atomic_read(&s->sk_rmem_alloc), + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); out: return 0; diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index f7a53b2..590b839 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1748,8 +1748,7 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { /* Protocol layer */ case TIOCOUTQ: { - long amount = sk->sk_sndbuf - - atomic_read(&sk->sk_wmem_alloc); + long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 61b35b9..da0f64f 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1690,7 +1690,8 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCOUTQ: { long amount; - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; res = put_user(amount, (int __user *)argp); @@ -1780,8 +1781,8 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ); ax25_info.n2count = ax25->n2count; ax25_info.state = ax25->state; - ax25_info.rcv_q = atomic_read(&sk->sk_rmem_alloc); - ax25_info.snd_q = atomic_read(&sk->sk_wmem_alloc); + ax25_info.rcv_q = sk_wmem_alloc_get(sk); + ax25_info.snd_q = sk_rmem_alloc_get(sk); ax25_info.vs = ax25->vs; ax25_info.vr = ax25->vr; ax25_info.va = ax25->va; @@ -1921,8 +1922,8 @@ static int ax25_info_show(struct seq_file *seq, void *v) if (ax25->sk != NULL) { seq_printf(seq, " %d %d %lu\n", - atomic_read(&ax25->sk->sk_wmem_alloc), - atomic_read(&ax25->sk->sk_rmem_alloc), + sk_wmem_alloc_get(ax25->sk), + sk_rmem_alloc_get(ax25->sk), sock_i_ino(ax25->sk)); } else { seq_puts(seq, " * * *\n"); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 02b9baa..0250e06 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -337,7 +337,7 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (sk->sk_state == BT_LISTEN) return -EINVAL; - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; err = put_user(amount, (int __user *) arg); diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index a5e3a59..d351b8d 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1240,7 +1240,7 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return val; case TIOCOUTQ: - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; err = put_user(amount, (int __user *)arg); diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 1779677..14d3984 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -126,7 +126,8 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) switch (cmd) { case SIOCOUTQ: { - int amount = atomic_read(&sk->sk_wmem_alloc); + int amount = sk_wmem_alloc_get(sk); + return put_user(amount, (int __user *)arg); } diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index b0b2735..a706a47 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -156,10 +156,10 @@ static int inet_csk_diag_fill(struct sock *sk, r->idiag_inode = sock_i_ino(sk); if (minfo) { - minfo->idiag_rmem = atomic_read(&sk->sk_rmem_alloc); + minfo->idiag_rmem = sk_rmem_alloc_get(sk); minfo->idiag_wmem = sk->sk_wmem_queued; minfo->idiag_fmem = sk->sk_forward_alloc; - minfo->idiag_tmem = atomic_read(&sk->sk_wmem_alloc); + minfo->idiag_tmem = sk_wmem_alloc_get(sk); } handler->idiag_get_info(sk, r, info); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 3dc9171..2979f14 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -799,7 +799,8 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) { switch (cmd) { case SIOCOUTQ: { - int amount = atomic_read(&sk->sk_wmem_alloc); + int amount = sk_wmem_alloc_get(sk); + return put_user(amount, (int __user *)arg); } case SIOCINQ: { @@ -935,8 +936,8 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) seq_printf(seq, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n", i, src, srcp, dest, destp, sp->sk_state, - atomic_read(&sp->sk_wmem_alloc), - atomic_read(&sp->sk_rmem_alloc), + sk_wmem_alloc_get(sp), + sk_rmem_alloc_get(sp), 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops)); } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 8f4158d..80e3812 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -840,7 +840,8 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) switch (cmd) { case SIOCOUTQ: { - int amount = atomic_read(&sk->sk_wmem_alloc); + int amount = sk_wmem_alloc_get(sk); + return put_user(amount, (int __user *)arg); } @@ -1721,8 +1722,8 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f, seq_printf(f, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d%n", bucket, src, srcp, dest, destp, sp->sk_state, - atomic_read(&sp->sk_wmem_alloc), - atomic_read(&sp->sk_rmem_alloc), + sk_wmem_alloc_get(sp), + sk_rmem_alloc_get(sp), 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops), len); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 36a090d..8b0b6f9 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1130,7 +1130,8 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg) switch(cmd) { case SIOCOUTQ: { - int amount = atomic_read(&sk->sk_wmem_alloc); + int amount = sk_wmem_alloc_get(sk); + return put_user(amount, (int __user *)arg); } case SIOCINQ: @@ -1236,8 +1237,8 @@ static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, sp->sk_state, - atomic_read(&sp->sk_wmem_alloc), - atomic_read(&sp->sk_rmem_alloc), + sk_wmem_alloc_get(sp), + sk_rmem_alloc_get(sp), 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fc333d8..023beda 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1061,8 +1061,8 @@ static void udp6_sock_seq_show(struct seq_file *seq, struct sock *sp, int bucket dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, sp->sk_state, - atomic_read(&sp->sk_wmem_alloc), - atomic_read(&sp->sk_rmem_alloc), + sk_wmem_alloc_get(sp), + sk_rmem_alloc_get(sp), 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 1627050..417b0e3 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1835,7 +1835,7 @@ static int ipx_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCOUTQ: - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; rc = put_user(amount, (int __user *)argp); diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c index 5ed97ad..5761784 100644 --- a/net/ipx/ipx_proc.c +++ b/net/ipx/ipx_proc.c @@ -280,8 +280,8 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v) } seq_printf(seq, "%08X %08X %02X %03d\n", - atomic_read(&s->sk_wmem_alloc), - atomic_read(&s->sk_rmem_alloc), + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); out: return 0; diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 3eb5bcc..5922feb 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -1762,7 +1762,8 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCOUTQ: { long amount; - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; if (put_user(amount, (unsigned int __user *)arg)) diff --git a/net/key/af_key.c b/net/key/af_key.c index 643c1be..dba9abd 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3662,8 +3662,8 @@ static int pfkey_seq_show(struct seq_file *f, void *v) seq_printf(f ,"%p %-6d %-6u %-6u %-6u %-6lu\n", s, atomic_read(&s->sk_refcnt), - atomic_read(&s->sk_rmem_alloc), - atomic_read(&s->sk_wmem_alloc), + sk_rmem_alloc_get(s), + sk_wmem_alloc_get(s), sock_i_uid(s), sock_i_ino(s) ); diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index d208b33..f97be47 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -134,8 +134,8 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v) seq_printf(seq, "@%02X ", llc->sap->laddr.lsap); llc_ui_format_mac(seq, llc->daddr.mac); seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap, - atomic_read(&sk->sk_wmem_alloc), - atomic_read(&sk->sk_rmem_alloc) - llc->copied_seq, + sk_wmem_alloc_get(sk), + sk_rmem_alloc_get(sk) - llc->copied_seq, sk->sk_state, sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1, llc->link); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 8b6bbb3..2936fa3 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1914,8 +1914,8 @@ static int netlink_seq_show(struct seq_file *seq, void *v) s->sk_protocol, nlk->pid, nlk->groups ? (u32)nlk->groups[0] : 0, - atomic_read(&s->sk_rmem_alloc), - atomic_read(&s->sk_wmem_alloc), + sk_rmem_alloc_get(s), + sk_wmem_alloc_get(s), nlk->cb, atomic_read(&s->sk_refcnt), atomic_read(&s->sk_drops) diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index cd91190..ce51ce0 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1205,7 +1205,7 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) long amount; lock_sock(sk); - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; release_sock(sk); @@ -1341,8 +1341,8 @@ static int nr_info_show(struct seq_file *seq, void *v) nr->n2count, nr->n2, nr->window, - atomic_read(&s->sk_wmem_alloc), - atomic_read(&s->sk_rmem_alloc), + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : 0L); bh_unlock_sock(s); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4f76e55..ebe5718 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1987,7 +1987,8 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, switch (cmd) { case SIOCOUTQ: { - int amount = atomic_read(&sk->sk_wmem_alloc); + int amount = sk_wmem_alloc_get(sk); + return put_user(amount, (int __user *)arg); } case SIOCINQ: diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 4dd9a7d..6bd8e938 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1309,7 +1309,8 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCOUTQ: { long amount; - amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0) amount = 0; return put_user(amount, (unsigned int __user *) argp); @@ -1480,8 +1481,8 @@ static int rose_info_show(struct seq_file *seq, void *v) rose->hb / HZ, ax25_display_timer(&rose->idletimer) / (60 * HZ), rose->idle / (60 * HZ), - atomic_read(&s->sk_wmem_alloc), - atomic_read(&s->sk_rmem_alloc), + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : 0L); } diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index 266151a..18d85d2 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -349,13 +349,13 @@ META_COLLECTOR(int_sk_type) META_COLLECTOR(int_sk_rmem_alloc) { SKIP_NONLOCAL(skb); - dst->value = atomic_read(&skb->sk->sk_rmem_alloc); + dst->value = sk_rmem_alloc_get(skb->sk); } META_COLLECTOR(int_sk_wmem_alloc) { SKIP_NONLOCAL(skb); - dst->value = atomic_read(&skb->sk->sk_wmem_alloc); + dst->value = sk_wmem_alloc_get(skb->sk); } META_COLLECTOR(int_sk_omem_alloc) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 0f01e5d..35ba035 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -130,7 +130,7 @@ static inline int sctp_wspace(struct sctp_association *asoc) if (asoc->ep->sndbuf_policy) amt = asoc->sndbuf_used; else - amt = atomic_read(&asoc->base.sk->sk_wmem_alloc); + amt = sk_wmem_alloc_get(asoc->base.sk); if (amt >= asoc->base.sk->sk_sndbuf) { if (asoc->base.sk->sk_userlocks & SOCK_SNDBUF_LOCK) @@ -6523,7 +6523,7 @@ static int sctp_writeable(struct sock *sk) { int amt = 0; - amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); + amt = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amt < 0) amt = 0; return amt; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 9dcc6e7..36d4e44 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1946,7 +1946,7 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case SIOCOUTQ: - amount = atomic_read(&sk->sk_wmem_alloc); + amount = sk_wmem_alloc_get(sk); err = put_user(amount, (int __user *)arg); break; case SIOCINQ: diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 8cd2390..21cdc87 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1271,8 +1271,8 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCOUTQ: { - int amount = sk->sk_sndbuf - - atomic_read(&sk->sk_wmem_alloc); + int amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + if (amount < 0) amount = 0; rc = put_user(amount, (unsigned int __user *)argp); diff --git a/net/x25/x25_proc.c b/net/x25/x25_proc.c index 1afa44d..0a04e62 100644 --- a/net/x25/x25_proc.c +++ b/net/x25/x25_proc.c @@ -163,8 +163,8 @@ static int x25_seq_socket_show(struct seq_file *seq, void *v) devname, x25->lci & 0x0FFF, x25->state, x25->vs, x25->vr, x25->va, x25_display_timer(s) / HZ, x25->t2 / HZ, x25->t21 / HZ, x25->t22 / HZ, x25->t23 / HZ, - atomic_read(&s->sk_wmem_alloc), - atomic_read(&s->sk_rmem_alloc), + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : 0L); out: return 0; -- cgit v0.10.2 From 81e2a3d5b75cbf0b42428b9d5a7cc7c85be9e7a7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 17 Jun 2009 19:06:12 -0700 Subject: atm: sk_wmem_alloc initial value is one commit 2b85a34e911bf483c27cfdd124aeb1605145dc80 (net: No more expensive sock_hold()/sock_put() on each tx) changed initial sk_wmem_alloc value. This broke net/atm since this protocol assumed a null initial value. This patch makes necessary changes. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/atm/common.c b/net/atm/common.c index d34edbe..c1c9793 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -62,15 +62,15 @@ static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size) struct sk_buff *skb; struct sock *sk = sk_atm(vcc); - if (atomic_read(&sk->sk_wmem_alloc) && !atm_may_send(vcc, size)) { + if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) { pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n", - atomic_read(&sk->sk_wmem_alloc), size, + sk_wmem_alloc_get(sk), size, sk->sk_sndbuf); return NULL; } - while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule(); - pr_debug("AlTx %d += %d\n", atomic_read(&sk->sk_wmem_alloc), - skb->truesize); + while (!(skb = alloc_skb(size, GFP_KERNEL))) + schedule(); + pr_debug("AlTx %d += %d\n", sk_wmem_alloc_get(sk), skb->truesize); atomic_add(skb->truesize, &sk->sk_wmem_alloc); return skb; } @@ -145,7 +145,7 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family) memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc)); memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc)); vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */ - atomic_set(&sk->sk_wmem_alloc, 0); + atomic_set(&sk->sk_wmem_alloc, 1); atomic_set(&sk->sk_rmem_alloc, 0); vcc->push = NULL; vcc->pop = NULL; diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 76ed3c8..4da8892 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c @@ -63,8 +63,7 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg error = -EINVAL; goto done; } - error = put_user(sk->sk_sndbuf - - atomic_read(&sk->sk_wmem_alloc), + error = put_user(sk->sk_sndbuf - sk_wmem_alloc_get(sk), (int __user *) argp) ? -EFAULT : 0; goto done; case SIOCINQ: diff --git a/net/atm/proc.c b/net/atm/proc.c index e7b3b273..38de5ff 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -204,8 +204,8 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) seq_printf(seq, "%3d", sk->sk_family); } seq_printf(seq, " %04lx %5d %7d/%7d %7d/%7d [%d]\n", vcc->flags, sk->sk_err, - atomic_read(&sk->sk_wmem_alloc), sk->sk_sndbuf, - atomic_read(&sk->sk_rmem_alloc), sk->sk_rcvbuf, + sk_wmem_alloc_get(sk), sk->sk_sndbuf, + sk_rmem_alloc_get(sk), sk->sk_rcvbuf, atomic_read(&sk->sk_refcnt)); } diff --git a/net/atm/raw.c b/net/atm/raw.c index b0a2d8c..cbfcc71 100644 --- a/net/atm/raw.c +++ b/net/atm/raw.c @@ -33,7 +33,7 @@ static void atm_pop_raw(struct atm_vcc *vcc,struct sk_buff *skb) struct sock *sk = sk_atm(vcc); pr_debug("APopR (%d) %d -= %d\n", vcc->vci, - atomic_read(&sk->sk_wmem_alloc), skb->truesize); + sk_wmem_alloc_get(sk), skb->truesize); atomic_sub(skb->truesize, &sk->sk_wmem_alloc); dev_kfree_skb_any(skb); sk->sk_write_space(sk); -- cgit v0.10.2 From 73a370795371e41f72aeca61656d47adeadf28e5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 17 Jun 2009 21:17:59 +0000 Subject: forcedeth: fix dma api mismatches forcedeth doesnt use properly dma api in its tx completion path and in nv_loopback_test() pci_map_single() should be paired with pci_unmap_single() pci_map_page() should be paired with pci_unmap_page() forcedeth xmit path uses pci_map_single() & pci_map_page(), but tx completion path only uses pci_unmap_single() nv_loopback_test() uses pci_map_single() & pci_unmap_page() Add a dma_single field in struct nv_skb_map, and define a helper function nv_unmap_txskb Signed-off-by: Eric Dumazet CC: Ayaz Abdulla Signed-off-by: David S. Miller diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index b60a304..1094d29 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -719,7 +719,8 @@ static const struct register_test nv_registers_test[] = { struct nv_skb_map { struct sk_buff *skb; dma_addr_t dma; - unsigned int dma_len; + unsigned int dma_len:31; + unsigned int dma_single:1; struct ring_desc_ex *first_tx_desc; struct nv_skb_map *next_tx_ctx; }; @@ -1912,6 +1913,7 @@ static void nv_init_tx(struct net_device *dev) np->tx_skb[i].skb = NULL; np->tx_skb[i].dma = 0; np->tx_skb[i].dma_len = 0; + np->tx_skb[i].dma_single = 0; np->tx_skb[i].first_tx_desc = NULL; np->tx_skb[i].next_tx_ctx = NULL; } @@ -1930,23 +1932,30 @@ static int nv_init_ring(struct net_device *dev) return nv_alloc_rx_optimized(dev); } -static int nv_release_txskb(struct net_device *dev, struct nv_skb_map* tx_skb) +static void nv_unmap_txskb(struct fe_priv *np, struct nv_skb_map *tx_skb) { - struct fe_priv *np = netdev_priv(dev); - if (tx_skb->dma) { - pci_unmap_page(np->pci_dev, tx_skb->dma, - tx_skb->dma_len, - PCI_DMA_TODEVICE); + if (tx_skb->dma_single) + pci_unmap_single(np->pci_dev, tx_skb->dma, + tx_skb->dma_len, + PCI_DMA_TODEVICE); + else + pci_unmap_page(np->pci_dev, tx_skb->dma, + tx_skb->dma_len, + PCI_DMA_TODEVICE); tx_skb->dma = 0; } +} + +static int nv_release_txskb(struct fe_priv *np, struct nv_skb_map *tx_skb) +{ + nv_unmap_txskb(np, tx_skb); if (tx_skb->skb) { dev_kfree_skb_any(tx_skb->skb); tx_skb->skb = NULL; return 1; - } else { - return 0; } + return 0; } static void nv_drain_tx(struct net_device *dev) @@ -1964,10 +1973,11 @@ static void nv_drain_tx(struct net_device *dev) np->tx_ring.ex[i].bufhigh = 0; np->tx_ring.ex[i].buflow = 0; } - if (nv_release_txskb(dev, &np->tx_skb[i])) + if (nv_release_txskb(np, &np->tx_skb[i])) dev->stats.tx_dropped++; np->tx_skb[i].dma = 0; np->tx_skb[i].dma_len = 0; + np->tx_skb[i].dma_single = 0; np->tx_skb[i].first_tx_desc = NULL; np->tx_skb[i].next_tx_ctx = NULL; } @@ -2171,6 +2181,7 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) np->put_tx_ctx->dma = pci_map_single(np->pci_dev, skb->data + offset, bcnt, PCI_DMA_TODEVICE); np->put_tx_ctx->dma_len = bcnt; + np->put_tx_ctx->dma_single = 1; put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma); put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); @@ -2196,6 +2207,7 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) np->put_tx_ctx->dma = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt, PCI_DMA_TODEVICE); np->put_tx_ctx->dma_len = bcnt; + np->put_tx_ctx->dma_single = 0; put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma); put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); @@ -2291,6 +2303,7 @@ static int nv_start_xmit_optimized(struct sk_buff *skb, struct net_device *dev) np->put_tx_ctx->dma = pci_map_single(np->pci_dev, skb->data + offset, bcnt, PCI_DMA_TODEVICE); np->put_tx_ctx->dma_len = bcnt; + np->put_tx_ctx->dma_single = 1; put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma)); put_tx->buflow = cpu_to_le32(dma_low(np->put_tx_ctx->dma)); put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); @@ -2317,6 +2330,7 @@ static int nv_start_xmit_optimized(struct sk_buff *skb, struct net_device *dev) np->put_tx_ctx->dma = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt, PCI_DMA_TODEVICE); np->put_tx_ctx->dma_len = bcnt; + np->put_tx_ctx->dma_single = 0; put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma)); put_tx->buflow = cpu_to_le32(dma_low(np->put_tx_ctx->dma)); put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); @@ -2434,10 +2448,7 @@ static int nv_tx_done(struct net_device *dev, int limit) dprintk(KERN_DEBUG "%s: nv_tx_done: flags 0x%x.\n", dev->name, flags); - pci_unmap_page(np->pci_dev, np->get_tx_ctx->dma, - np->get_tx_ctx->dma_len, - PCI_DMA_TODEVICE); - np->get_tx_ctx->dma = 0; + nv_unmap_txskb(np, np->get_tx_ctx); if (np->desc_ver == DESC_VER_1) { if (flags & NV_TX_LASTPACKET) { @@ -2502,10 +2513,7 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit) dprintk(KERN_DEBUG "%s: nv_tx_done_optimized: flags 0x%x.\n", dev->name, flags); - pci_unmap_page(np->pci_dev, np->get_tx_ctx->dma, - np->get_tx_ctx->dma_len, - PCI_DMA_TODEVICE); - np->get_tx_ctx->dma = 0; + nv_unmap_txskb(np, np->get_tx_ctx); if (flags & NV_TX2_LASTPACKET) { if (!(flags & NV_TX2_ERROR)) @@ -5091,7 +5099,7 @@ static int nv_loopback_test(struct net_device *dev) dprintk(KERN_DEBUG "%s: loopback - did not receive test packet\n", dev->name); } - pci_unmap_page(np->pci_dev, test_dma_addr, + pci_unmap_single(np->pci_dev, test_dma_addr, (skb_end_pointer(tx_skb) - tx_skb->data), PCI_DMA_TODEVICE); dev_kfree_skb_any(tx_skb); -- cgit v0.10.2 From d173346040af497af22818dfc48cff369e67cf50 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Wed, 17 Jun 2009 17:27:24 +0000 Subject: netxen: fix detection of cut-thru firmware mode Fix the detection of cut-thru mode of the hardware (direct dma to host) to mode configured in SRE (ingress block) rather than onboard memory control. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index 7f0ddbf..3cc0478 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -355,6 +355,7 @@ enum { #define NETXEN_HW_CRB_HUB_AGT_ADR_LPC \ ((NETXEN_HW_H6_CH_HUB_ADR << 7) | NETXEN_HW_LPC_CRB_AGT_ADR) +#define NETXEN_SRE_MISC (NETXEN_CRB_SRE + 0x0002c) #define NETXEN_SRE_INT_STATUS (NETXEN_CRB_SRE + 0x00034) #define NETXEN_SRE_PBI_ACTIVE_STATUS (NETXEN_CRB_SRE + 0x01014) #define NETXEN_SRE_L1RE_CTL (NETXEN_CRB_SRE + 0x03000) diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index 42ffb825..9702509 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -2041,8 +2041,8 @@ void netxen_nic_get_firmware_info(struct netxen_adapter *adapter) fw_major, fw_minor, fw_build); if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { - i = NXRD32(adapter, NETXEN_MIU_MN_CONTROL); - adapter->ahw.cut_through = (i & 0x4) ? 1 : 0; + i = NXRD32(adapter, NETXEN_SRE_MISC); + adapter->ahw.cut_through = (i & 0x8000) ? 1 : 0; dev_info(&pdev->dev, "firmware running in %s mode\n", adapter->ahw.cut_through ? "cut-through" : "legacy"); } -- cgit v0.10.2 From cb2107be43d2fc5eadec58b92b54bf32c00bfff3 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Wed, 17 Jun 2009 17:27:25 +0000 Subject: netxen: fix tx ring accounting This forces every update of tx ring producer to check for availability of space for next full TSO command. Earlier firmware control commands didn't care to pause tx queue. Stop the tx queue if there's not enough space to transmit one full LSO command left on the tx ring after current transmit. This avoids returning NETDEV_TX_BUSY after checking distance between producer and consumer on every cpu. Restart the tx queue only if we have cleaned up enough tx descriptors. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index ab11c2b..970cede 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -169,6 +169,7 @@ #define MAX_NUM_CARDS 4 #define MAX_BUFFERS_PER_CMD 32 +#define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + 4) /* * Following are the states of the Phantom. Phantom will set them and @@ -1436,7 +1437,7 @@ int netxen_nic_set_mac(struct net_device *netdev, void *p); struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev); void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter, - struct nx_host_tx_ring *tx_ring, uint32_t crb_producer); + struct nx_host_tx_ring *tx_ring); /* * NetXen Board information @@ -1538,6 +1539,14 @@ dma_watchdog_wakeup(struct netxen_adapter *adapter) } +static inline u32 netxen_tx_avail(struct nx_host_tx_ring *tx_ring) +{ + smp_mb(); + return find_diff_among(tx_ring->producer, + tx_ring->sw_consumer, tx_ring->num_desc); + +} + int netxen_get_flash_mac_addr(struct netxen_adapter *adapter, __le64 *mac); int netxen_p3_get_mac_addr(struct netxen_adapter *adapter, __le64 *mac); extern void netxen_change_ringparam(struct netxen_adapter *adapter); diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index 9702509..ce3b89d 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -488,7 +488,7 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter, tx_ring->producer = producer; - netxen_nic_update_cmd_producer(adapter, tx_ring, producer); + netxen_nic_update_cmd_producer(adapter, tx_ring); netif_tx_unlock_bh(adapter->netdev); diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 6f77ad5..bdb143d 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -1292,7 +1292,6 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) return 1; sw_consumer = tx_ring->sw_consumer; - barrier(); /* hw_consumer can change underneath */ hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); while (sw_consumer != hw_consumer) { @@ -1319,14 +1318,15 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) break; } - tx_ring->sw_consumer = sw_consumer; - if (count && netif_running(netdev)) { + tx_ring->sw_consumer = sw_consumer; + smp_mb(); + if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) { netif_tx_lock(netdev); - netif_wake_queue(netdev); - smp_mb(); + if (netxen_tx_avail(tx_ring) > TX_STOP_THRESH) + netif_wake_queue(netdev); netif_tx_unlock(netdev); } } @@ -1343,7 +1343,6 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) * There is still a possible race condition and the host could miss an * interrupt. The card has to take care of this. */ - barrier(); /* hw_consumer can change underneath */ hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); done = (sw_consumer == hw_consumer); spin_unlock(&adapter->tx_clean_lock); diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 98737ef..71daa3d 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -107,9 +107,14 @@ static uint32_t crb_cmd_producer[4] = { void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter, - struct nx_host_tx_ring *tx_ring, u32 producer) + struct nx_host_tx_ring *tx_ring) { - NXWR32(adapter, tx_ring->crb_cmd_producer, producer); + NXWR32(adapter, tx_ring->crb_cmd_producer, tx_ring->producer); + + if (netxen_tx_avail(tx_ring) <= TX_STOP_THRESH) { + netif_stop_queue(adapter->netdev); + smp_mb(); + } } static uint32_t crb_cmd_consumer[4] = { @@ -119,9 +124,9 @@ static uint32_t crb_cmd_consumer[4] = { static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter, - struct nx_host_tx_ring *tx_ring, u32 consumer) + struct nx_host_tx_ring *tx_ring) { - NXWR32(adapter, tx_ring->crb_cmd_consumer, consumer); + NXWR32(adapter, tx_ring->crb_cmd_consumer, tx_ring->sw_consumer); } static uint32_t msi_tgt_status[8] = { @@ -900,8 +905,11 @@ netxen_nic_attach(struct netxen_adapter *adapter) tx_ring->crb_cmd_producer = crb_cmd_producer[adapter->portnum]; tx_ring->crb_cmd_consumer = crb_cmd_consumer[adapter->portnum]; - netxen_nic_update_cmd_producer(adapter, tx_ring, 0); - netxen_nic_update_cmd_consumer(adapter, tx_ring, 0); + tx_ring->producer = 0; + tx_ring->sw_consumer = 0; + + netxen_nic_update_cmd_producer(adapter, tx_ring); + netxen_nic_update_cmd_consumer(adapter, tx_ring); } for (ring = 0; ring < adapter->max_rds_rings; ring++) { @@ -1362,7 +1370,7 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) dma_addr_t temp_dma; int i, k; - u32 producer, consumer; + u32 producer; int frag_count, no_of_desc; u32 num_txd = tx_ring->num_desc; bool is_tso = false; @@ -1372,15 +1380,13 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* 4 fragments per cmd des */ no_of_desc = (frag_count + 3) >> 2; - producer = tx_ring->producer; - smp_mb(); - consumer = tx_ring->sw_consumer; - if ((no_of_desc+2) >= find_diff_among(producer, consumer, num_txd)) { + if (unlikely(no_of_desc + 2) > netxen_tx_avail(tx_ring)) { netif_stop_queue(netdev); - smp_mb(); return NETDEV_TX_BUSY; } + producer = tx_ring->producer; + hwdesc = &tx_ring->desc_head[producer]; netxen_clear_cmddesc((u64 *)hwdesc); pbuf = &tx_ring->cmd_buf_arr[producer]; @@ -1493,7 +1499,7 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) tx_ring->producer = producer; adapter->stats.txbytes += skb->len; - netxen_nic_update_cmd_producer(adapter, tx_ring, producer); + netxen_nic_update_cmd_producer(adapter, tx_ring); adapter->stats.xmitcalled++; -- cgit v0.10.2